v2.0.0 - Complete extension redesign

Features:
- New modern dark UI with gradient accents
- Hostname support for custom proxy (e.g. relayup.local)
- Full private IP range bypass (10.x, 172.16-31.x, 192.168.x)
- WebRTC Leak Protection setting
- Kill Switch - block traffic if proxy disconnects
- Auto-connect on browser startup
- Custom proxy authentication (username/password)
- Bypass list for custom exceptions
- Local Network Access for printers, NAS, routers, etc
- Multiple proxy sources with automatic fallback (Arweave → GitBros → GitHub)
- Arweave as default proxy source for decentralized, permanent storage
- Auto-update interval for proxy list
This commit is contained in:
johnysigma 2026-01-23 16:54:51 +02:00
parent 9912462169
commit 2e28314d52
25 changed files with 5150 additions and 1397 deletions

248
README.md
View File

@ -1,169 +1,154 @@
# ANyONe Extension - Manage Socks5 Proxy Settings
# ANyONe Extension v2.0
## Overview
**The ANyONe Proxy Extension** is a powerful Chromium-based browser extension designed to help users manage and switch between different proxy settings effortlessly. It offers:
- **Quick access** through the browser's toolbar.
- **Detailed control** via an options page.
Additionally, the extension features a **dApp Store**, enabling users to access decentralized applications directly from the extension. **This tool also simplifies access to the Socks5 Proxy of your official ANyONe router.** Created by DeBros, it is a community-driven project and **not an official ANyONe product**.
**Note:** It's recommended to leverage local Socks5 Proxies within our internal network for optimal security and performance. Always confirm you are within the ANyONe network by pressing the 'Check ANyONe' button.
##
<p align="center">
<img src="https://git.debros.io/DeBros/anyone-extension/raw/branch/main/images/screenshot.png" alt="Alt Text" width="800">
</p>
A privacy-focused Chromium browser extension for managing SOCKS5 proxy connections to the ANyONe network.
## Features
### 1. Quick Proxy Toggle
- **Enable/Disable Proxy**: A toggle switch in the popup allows users to quickly turn the proxy on or off.
- **Public Proxies**:
- **Default Activation**: Automatically uses community-contributed public proxy servers when no custom settings are applied.
- **Dynamic List**: Easily update the proxy list with a dedicated button within the extension.
### Connection Modes
### 2. Custom Proxy Settings via Options Page
#### Public Proxies Mode
- **One-click connect** to community-powered ANyONe proxy servers
- **Auto-selection** of the fastest available proxy
- **Load balancing** with "Next Proxy" button to switch servers
- **Multiple sources**: Arweave, GitBros, or GitHub
- **Automatic fallback**: If one source fails, tries the next automatically
- **Access Custom Settings**: Users can configure custom proxies by navigating to the options page.
- **Host and Port**: Specify the host IP and port number for your custom proxy.
- **No Proxy Exceptions**: Define specific websites or local addresses where the proxy should not be applied.
- **Detailed Configuration**: The options page allows for:
- Setting up custom proxy configurations.
- Managing exceptions to proxy use.
- Saving and applying changes for a tailored browsing experience.
#### Custom Proxy Mode
- **Full SOCKS5 configuration** with IP/hostname and port
- **Authentication support** for proxies requiring username/password
- **Test connection** before connecting
### 3. Proxy Status Indication
- **Status Messages**: The extension provides clear feedback on the current proxy status both in the popup and options page, including:
- Whether a proxy is enabled or disabled.
- The type of proxy in use (public or custom).
- The specific host and port being routed through.
### Privacy & Security
### 4. External Links
- **Check ANyONe**: A button to directly check the external IP and proxy status via the ANyONe service.
- **Credits**: Links to the developer's website and the GitHub repository for the extension.
- **Popup**: Links to the developer's website, X account, GitHub repository for the extension, and ANyONe website.
| Feature | Description |
|---------|-------------|
| **WebRTC Leak Protection** | Prevents real IP leaks through WebRTC |
| **Kill Switch** | Blocks all traffic if proxy connection drops unexpectedly |
| **Local Network Access** | Toggle to allow/block access to local devices (printers, NAS, routers) while connected |
| **Bypass List** | Domains and IPs that skip the proxy
## Usage
### Settings & Customization
### Enabling/Disabling Proxy
- Click the extension icon to open the popup.
- Use the toggle switch to enable or disable the proxy.
- The status message will update to reflect the current state.
- **Auto-connect on startup** - Automatically connect when browser starts
- **Default connection mode** - Choose Public or Custom as default
- **Proxy source selection** - Arweave (default), GitBros, or GitHub with automatic fallback
- **Update interval** - Manual, hourly, or periodic auto-updates of proxy list
### Public Proxies:
### User Interface
- **Default Usage**: When no custom settings are specified, the extension automatically uses public proxy servers, which are contributed and maintained by the ANyONe community.
- **Update Mechanism**: You can easily refresh the proxy list directly within the extension using an update button.
- **View Proxies**: To see the current list of community-powered public proxies, visit [https://git.debros.io/DeBros/anyone-proxy-list](https://git.debros.io/DeBros/anyone-proxy-list).
### Setting a Custom Proxy
- Navigate to the options page by clicking the "Custom Settings" button in the popup.
- Enter the Host IP and Port for your custom proxy.
- Optionally, specify IP addresses or domains that should bypass the proxy in the "No Proxy for" field.
- Click "Save & Enable" to apply your settings.
### Disabling the Proxy
- From the options page, click "Disable" to turn off the proxy settings, or from the popup, turn the toggle off.
### Accessing the dApp Store
- Click the "dApp Store" button in the popup to open the dApp Store page.
- The dApp Store page will display available decentralized applications.
- **Modern dark theme** with clean, intuitive design
- **Real-time status** showing connection state and current proxy
- **Quick actions** - Check IP, refresh proxies, access settings
- **Toast notifications** for instant feedback
## Installation
### 1. Clone or Download the Repository
You have two options to get the extension on your system:
- **Option A: Clone with Git**
### Option 1: Clone with Git
```bash
git clone "https://git.debros.io/DeBros/anyone-extension.git"
```
- **Option B: [Download ZIP](https://git.debros.io/DeBros/anyone-extension/archive/main.zip)**
### Option 2: Download ZIP
[Download ZIP](https://git.debros.io/DeBros/anyone-extension/archive/main.zip)
### 2. Load Unpacked Extension in Chromium-based Browser
- Open your browser and navigate to the extensions page.
- Enable "Developer mode".
- Depending on your method in step 1:
- If you cloned the repository, click "Load unpacked" and select the cloned directory containing the `manifest.json` file.
- If you downloaded and unpacked, drag and drop the unpacked folder into the extensions window.
### Load in Browser
1. Open your Chromium-based browser (Chrome, Brave, Edge, etc.)
2. Navigate to `chrome://extensions/`
3. Enable **Developer mode** (toggle in top right)
4. Click **Load unpacked**
5. Select the extension folder containing `manifest.json`
## Contribution
Contributions are welcome! Please fork the repository and submit pull requests for any enhancements or bug fixes.
## Usage
For questions or further discussion, reach out to us on <a href="https://t.me/debrosportal" target="_blank">Telegram</a>
### Quick Start
1. Click the extension icon in your browser toolbar
2. Choose **Public** or **Custom** mode
3. Click the **Connect** button
### Public Proxies
- Automatically fetches and connects to community proxies
- Use **Next Proxy** to switch to a different server
- Click **Refresh** to update the proxy list
### Custom Proxy
1. Enter your SOCKS5 proxy IP/hostname and port
2. (Settings/Optional) Add username and password for authentication
3. (Settings/Optional) Configure bypass list for specific domains
4. Click **Test Connection** to verify, then **Connect**
### Check Your Connection
Click **Check IP** to verify your connection is routed through the ANyONe network.
## Proxy Sources
The extension fetches proxy lists from multiple sources with automatic fallback:
| Source | URL | Type |
|--------|-----|------|
| **Arweave** (Default) | [arweave.net/FjxfWIbS...](https://arweave.net/FjxfWIbSnZb7EaJWbeuWCsBBFWjTppfS3_KHxUP__B8) | Decentralized, permanent |
| **GitBros** | [git.debros.io/DeBros/anyone-proxy-list](https://git.debros.io/DeBros/anyone-proxy-list) | Self-hosted Git |
| **GitHub** | [github.com/DeBrosOfficial/anyone-proxy-list](https://github.com/DeBrosOfficial/anyone-proxy-list) | Centralized backup |
**Fallback order:** Arweave → GitBros → GitHub
## Privacy Settings Explained
### WebRTC Leak Protection
WebRTC can expose your real IP even when using a proxy. Enable this to prevent leaks.
### Kill Switch
When enabled, if your proxy connection drops unexpectedly, all internet traffic will be blocked until you reconnect or disable the kill switch. This prevents accidental exposure of your real IP.
### Local Network Access
- **Enabled (default)**: Access local devices (192.168.x.x, 10.x.x.x, .local domains) directly
- **Disabled**: All traffic goes through proxy, local devices unreachable
## Technical Details
- **Manifest V3** compliant
- **SOCKS5** proxy protocol
- **Chrome Proxy API** for system-level proxy configuration
- **Chrome Privacy API** for WebRTC protection
## Contributing
Contributions are welcome! Please fork the repository and submit pull requests.
For questions or discussion, join us on [Telegram](https://t.me/debrosportal).
---
# Optional: Enhancing Security with Custom DNS Configuration
## Optional: Secure DNS Configuration
Manually configuring your network's DNS can significantly boost your online privacy and security. Below, you'll find a selection of well-regarded, secure DNS servers that provide enhanced protection and privacy features:
Enhance your privacy by using secure DNS servers:
## Cloudflare DNS (1.1.1.1)
### Recommended DNS Providers
- **IPv4**: 1.1.1.1 and 1.0.0.1
- **IPv6**: 2606:4700:4700::1111 and 2606:4700:4700::1001
- **Features**: Fast performance, does not log DNS queries, supports DNS over HTTPS (DoH) and DNS over TLS (DoT).
- **Website**: [https://1.1.1.1/dns](https://1.1.1.1/dns)
## Quad9 (9.9.9.9)
- **IPv4**: 9.9.9.9 and 149.112.112.112
- **IPv6**: 2620:fe::9 and 2620:fe::fe:9
- **Features**: Offers protection against malware and phishing, privacy with no logging of DNS queries. Supports DoT.
- **Website**: [https://quad9.net](https://quad9.net)
## Mullvad DNS
- **IPv4**: 194.242.2.1 and 194.242.2.2
- **IPv6**: 2a07:e340::1 and 2a07:e340::2
- **Features**: Complete privacy with no logging, supports DoH and DoT. Ideal for users concerned with anonymity.
- **Website**: [https://mullvad.net/en](https://mullvad.net/en)
## AdGuard DNS
- **IPv4**: 94.140.14.14 and 94.140.15.15 (without filters), 176.103.130.130 and 176.103.130.131 (with filters)
- **IPv6**: 2a10:50c0::ad1:ff and 2a10:50c0::ad2:ff (without filters), 2a10:50c0::bad1:ff and 2a10:50c0::bad2:ff (with filters)
- **Features**: Provides protection against ads, trackers, and phishing, as well as privacy with no logging.
- **Website**: [https://adguard-dns.io](https://adguard-dns.io)
## How to Configure:
- **For Windows**: Go to "Settings" > "Network & Internet" > "Change adapter options", right-click on your connection, select "Properties", then select "Internet Protocol Version 4 (TCP/IPv4)" or "Internet Protocol Version 6 (TCP/IPv6)" and enter the DNS addresses you want.
- **For macOS**: Navigate to "System Preferences" > "Network", select your connection, click on the "Advanced" button, go to the "DNS" tab, and add your DNS addresses.
- **For Linux**: Depending on the distribution, you can usually modify the `/etc/resolv.conf` file to add DNS addresses.
- **For Routers**: You'll typically find DNS settings in the advanced settings of your router. This will change the DNS for all devices connected to the network.
| Provider | IPv4 | Features |
|----------|------|----------|
| **Cloudflare** | 1.1.1.1, 1.0.0.1 | Fast, no logging, DoH/DoT |
| **Quad9** | 9.9.9.9, 149.112.112.112 | Malware protection, no logging |
| **Mullvad** | 194.242.2.2 | Full privacy, no logging |
| **AdGuard** | 94.140.14.14, 94.140.15.15 | Ad blocking, no logging |
---
# ANyONe Protocol: Connection and Setup Guide
## ANyONe Protocol Resources
**About ANyONe Protocol**: ANyONe is a decentralized network protocol focused on providing privacy, security, and freedom on the internet. Whether you're looking to browse anonymously or secure your online communications, ANyONe offers versatile solutions for different needs.
Explore multiple ways to interact with the ANyONe, whether you're connecting directly from your OS, setting up your own relay for personalized control, or using dedicated hardware for an optimized experience. Here's your guide:
- **Linux**: Enjoy a seamless one-click setup. Learn more in the [Linux Connection Guide](https://docs.anyone.io/connect/connecting-to-linux).
- **macOS**: Connect easily with or without npm. Check the [macOS Connection Guide](https://docs.anyone.io/connect/connecting-to-macos).
- **Windows**: Benefit from a straightforward one-click setup. See the [Windows Connection Guide](https://docs.anyone.io/connect/connecting-to-windows).
**Setting Up Your Own Relay**: For those interested in customizing your network participation or contributing to the ANyONe ecosystem, follow the [Relay Setup Guide](https://docs.anyone.io/relay).
**Dedicated Hardware**: For a user-friendly, plug-and-play experience, ANyONe offers specialized hardware like the Anyone Router. This hardware is designed for non-technical users to contribute to and use the network seamlessly, offering:
- **Ease of Use**: Power on, connect to Wi-Fi or Ethernet, and earn tokens for contributing your bandwidth.
- **Security**: Includes custom components like encryption chips similar to those in hardware wallets.
- **Diversity**: Enhances network coverage across various ISPs and introduces more independent operators.
Check out the [Hardware Setup Guide](https://docs.anyone.io/hardware) or visit [Anyone Hardware](https://www.anyone.io/hardware) to learn more and pre-order.
- [Linux Connection Guide](https://docs.anyone.io/connect/connecting-to-linux)
- [macOS Connection Guide](https://docs.anyone.io/connect/connecting-to-macos)
- [Windows Connection Guide](https://docs.anyone.io/connect/connecting-to-windows)
- [Relay Setup Guide](https://docs.anyone.io/relay)
- [Hardware Setup Guide](https://docs.anyone.io/hardware)
---
###
<br clear="both">
<div align="center">
**Created by [DeBros](https://debros.io)** | **Version 2.0.0**
[![Support DeBros](https://img.shields.io/badge/Support-DeBros-cyan?style=for-the-badge)](https://debros.io/donate)
<a href="https://linktr.ee/debrosofficial" target="_blank">
<img src="https://img.shields.io/static/v1?message=Linktree&logo=linktree&label=&color=1de9b6&logoColor=white&labelColor=&style=for-the-badge" height="35" alt="linktree logo" />
</a>
@ -176,6 +161,5 @@ Explore multiple ways to interact with the ANyONe, whether you're connecting dir
<a href="https://www.youtube.com/@DeBrosOfficial" target="_blank">
<img src="https://img.shields.io/static/v1?message=Youtube&logo=youtube&label=&color=FF0000&logoColor=white&labelColor=&style=for-the-badge" height="35" alt="youtube logo" />
</a>
</div>
###
</div>

395
css/common.css Normal file
View File

@ -0,0 +1,395 @@
/* ANyONe Extension v2 - Common Components */
@import url('variables.css');
/* Reset & Base */
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font-family);
font-size: var(--font-size-md);
color: var(--color-text-primary);
background-color: var(--color-bg-primary);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
/* Typography */
h1, h2, h3, h4 {
font-weight: var(--font-weight-semibold);
color: var(--color-text-primary);
}
h1 { font-size: var(--font-size-2xl); }
h2 { font-size: var(--font-size-xl); }
h3 { font-size: var(--font-size-lg); }
h4 { font-size: var(--font-size-md); }
.text-muted {
color: var(--color-text-muted);
}
.text-secondary {
color: var(--color-text-secondary);
}
.text-success {
color: var(--color-success);
}
.text-error {
color: var(--color-error);
}
.text-warning {
color: var(--color-warning);
}
/* Cards */
.card {
background: var(--color-bg-secondary);
border-radius: var(--radius-lg);
padding: var(--spacing-md);
border: 1px solid var(--color-border);
}
.card-header {
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: var(--spacing-md);
}
/* Buttons */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--spacing-sm);
padding: var(--spacing-md) var(--spacing-lg);
font-size: var(--font-size-md);
font-weight: var(--font-weight-medium);
border: none;
border-radius: var(--radius-md);
cursor: pointer;
transition: all var(--transition-fast);
text-decoration: none;
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.btn .icon {
width: 16px;
height: 16px;
}
.btn-primary {
background: var(--color-primary);
color: var(--color-text-primary);
}
.btn-primary:hover:not(:disabled) {
background: var(--color-primary-light);
box-shadow: var(--shadow-glow);
}
.btn-secondary {
background: var(--color-bg-tertiary);
color: var(--color-text-primary);
border: 1px solid var(--color-border);
}
.btn-secondary:hover:not(:disabled) {
background: var(--color-bg-hover);
border-color: var(--color-border-light);
}
.btn-success {
background: var(--color-success);
color: var(--color-bg-primary);
}
.btn-success:hover:not(:disabled) {
background: var(--color-secondary-light);
box-shadow: var(--shadow-glow-success);
}
.btn-icon {
width: 40px;
height: 40px;
padding: 0;
border-radius: var(--radius-md);
}
.btn-sm {
padding: var(--spacing-sm) var(--spacing-md);
font-size: var(--font-size-sm);
}
.btn-lg {
padding: var(--spacing-lg) var(--spacing-xl);
font-size: var(--font-size-lg);
}
/* Connect Button (Special) */
.btn-connect {
width: 120px;
height: 120px;
border-radius: 50%;
background: linear-gradient(135deg, var(--color-primary), var(--color-primary-dark));
color: var(--color-text-primary);
font-size: var(--font-size-lg);
font-weight: var(--font-weight-semibold);
border: 3px solid var(--color-primary-light);
transition: all var(--transition-normal);
}
.btn-connect:hover:not(:disabled) {
transform: scale(1.05);
box-shadow: var(--shadow-glow);
}
.btn-connect.connected {
background: linear-gradient(135deg, var(--color-success), var(--color-secondary-dark));
border-color: var(--color-secondary-light);
}
.btn-connect.connected:hover:not(:disabled) {
box-shadow: var(--shadow-glow-success);
}
.btn-connect.connecting {
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.05); opacity: 0.8; }
}
/* Inputs */
.input {
width: 100%;
padding: var(--spacing-md);
font-size: var(--font-size-md);
font-family: var(--font-family);
color: var(--color-text-primary);
background: var(--color-bg-tertiary);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
transition: all var(--transition-fast);
}
.input:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 3px rgba(3, 189, 197, 0.2);
}
.input::placeholder {
color: var(--color-text-muted);
}
.input-group {
margin-bottom: var(--spacing-md);
}
.input-label {
display: block;
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
color: var(--color-text-secondary);
margin-bottom: var(--spacing-xs);
}
/* Select / Dropdown */
.select {
width: 100%;
padding: var(--spacing-md);
font-size: var(--font-size-md);
font-family: var(--font-family);
color: var(--color-text-primary);
background: var(--color-bg-tertiary);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
cursor: pointer;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%238B8CA7' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 12px center;
padding-right: 36px;
}
.select:focus {
outline: none;
border-color: var(--color-primary);
}
/* Toggle Switch */
.toggle {
position: relative;
display: inline-block;
width: 48px;
height: 26px;
cursor: pointer;
flex-shrink: 0;
}
.toggle input {
opacity: 0;
width: 0;
height: 0;
position: absolute;
}
.toggle-slider {
position: absolute;
top: 0;
left: 0;
width: 48px;
height: 26px;
background: var(--color-bg-tertiary);
border: 1px solid var(--color-border);
border-radius: 13px;
transition: all var(--transition-fast);
overflow: visible;
}
.toggle-slider::before {
content: '';
position: absolute;
width: 18px;
height: 18px;
left: 3px;
top: 3px;
background: var(--color-text-muted);
border-radius: 50%;
transition: all var(--transition-fast);
}
.toggle input:checked + .toggle-slider {
background: var(--color-primary);
border-color: var(--color-primary);
}
.toggle input:checked + .toggle-slider::before {
left: 25px;
background: var(--color-text-primary);
}
/* Mode Tabs */
.mode-tabs {
display: flex;
background: var(--color-bg-tertiary);
border-radius: var(--radius-full);
padding: 4px;
gap: 4px;
overflow: hidden;
}
.mode-tab {
flex: 1;
padding: var(--spacing-sm) var(--spacing-md);
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
color: var(--color-text-muted);
background: transparent;
border: none;
border-radius: 9999px;
cursor: pointer;
transition: color var(--transition-fast), background var(--transition-fast);
outline: none;
-webkit-appearance: none;
appearance: none;
}
.mode-tab:hover:not(.active) {
color: var(--color-text-secondary);
background: var(--color-bg-hover);
}
.mode-tab:focus {
outline: none;
}
.mode-tab.active {
background: var(--color-primary);
color: var(--color-text-primary);
}
/* Badge */
.badge {
display: inline-flex;
align-items: center;
padding: 2px 8px;
font-size: var(--font-size-xs);
font-weight: var(--font-weight-medium);
border-radius: var(--radius-full);
background: var(--color-bg-tertiary);
color: var(--color-text-secondary);
}
.badge-success {
background: rgba(2, 175, 80, 0.15);
color: var(--color-success);
}
.badge-warning {
background: rgba(243, 156, 18, 0.15);
color: var(--color-warning);
}
.badge-error {
background: rgba(231, 76, 60, 0.15);
color: var(--color-error);
}
/* Divider */
.divider {
height: 1px;
background: var(--color-border);
margin: var(--spacing-lg) 0;
}
/* Loading Spinner */
.spinner {
width: 20px;
height: 20px;
border: 2px solid var(--color-bg-tertiary);
border-top-color: var(--color-primary);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Utility Classes */
.flex { display: flex; }
.flex-col { flex-direction: column; }
.items-center { align-items: center; }
.justify-center { justify-content: center; }
.justify-between { justify-content: space-between; }
.flex-wrap { flex-wrap: wrap; }
.gap-xs { gap: var(--spacing-xs); }
.gap-sm { gap: var(--spacing-sm); }
.gap-md { gap: var(--spacing-md); }
.gap-lg { gap: var(--spacing-lg); }
.w-full { width: 100%; }
.text-center { text-align: center; }
.mt-sm { margin-top: var(--spacing-sm); }
.mt-md { margin-top: var(--spacing-md); }
.mt-lg { margin-top: var(--spacing-lg); }
.mb-sm { margin-bottom: var(--spacing-sm); }
.mb-md { margin-bottom: var(--spacing-md); }
.mb-lg { margin-bottom: var(--spacing-lg); }

636
css/options.css Normal file
View File

@ -0,0 +1,636 @@
/* ANyONe Extension v2 - Options/Settings Page Styles */
@import url('common.css');
/* Page Container */
body {
min-height: 100vh;
}
.options-container {
max-width: 600px;
margin: 0 auto;
padding: var(--spacing-xl);
}
/* Header */
.options-header {
display: flex;
align-items: center;
gap: var(--spacing-lg);
margin-bottom: var(--spacing-2xl);
padding-bottom: var(--spacing-lg);
border-bottom: 1px solid var(--color-border);
}
.options-title {
font-size: 28px;
font-weight: var(--font-weight-semibold);
margin: 0;
}
.header-logo-img {
height: 38px;
width: auto;
}
/* Icons */
.icon {
width: 18px;
height: 18px;
stroke: currentColor;
flex-shrink: 0;
}
/* Toggle Switch - uses styles from common.css */
/* Settings Sections */
.settings-section {
margin-bottom: var(--spacing-2xl);
}
.section-title {
font-size: var(--font-size-sm);
font-weight: var(--font-weight-semibold);
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: var(--spacing-md);
}
.settings-card {
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
overflow: hidden;
}
/* Settings Row */
.setting-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--spacing-lg);
border-bottom: 1px solid var(--color-border);
transition: background var(--transition-fast);
}
.setting-row:last-child {
border-bottom: none;
}
.setting-row:hover {
background: var(--color-bg-tertiary);
}
.setting-info {
flex: 1;
}
.setting-label {
font-weight: var(--font-weight-medium);
color: var(--color-text-primary);
margin-bottom: 2px;
}
.setting-hint {
font-weight: normal;
font-size: var(--font-size-sm);
color: var(--color-text-muted);
}
.setting-desc {
font-size: var(--font-size-sm);
color: var(--color-text-muted);
}
.setting-control {
margin-left: var(--spacing-lg);
flex-shrink: 0;
display: flex;
align-items: center;
}
/* Settings with input */
.setting-row.vertical {
flex-direction: column;
align-items: stretch;
}
.setting-row.vertical .setting-control {
margin-left: 0;
margin-top: var(--spacing-md);
}
/* Select in settings */
.select {
padding: var(--spacing-sm) var(--spacing-md);
font-size: var(--font-size-sm);
color: #FFFFFF;
background-color: var(--color-bg-tertiary);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
cursor: pointer;
min-width: 160px;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%238B8CA7' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 12px center;
padding-right: 36px;
}
.select:focus {
outline: none;
border-color: var(--color-primary);
}
.select option {
background-color: #1A2E3D;
color: #FFFFFF;
padding: 8px;
}
/* Input */
.input {
padding: var(--spacing-sm) var(--spacing-md);
font-size: var(--font-size-base);
color: var(--color-text-primary);
background: var(--color-bg-tertiary);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
width: 100%;
}
.input:focus {
outline: none;
border-color: var(--color-primary);
}
/* Port Inputs */
.port-inputs {
display: flex;
gap: var(--spacing-md);
}
.port-input-group {
flex: 1;
}
.port-input-group label {
display: block;
font-size: var(--font-size-sm);
color: var(--color-text-muted);
margin-bottom: var(--spacing-xs);
}
.port-input-group input {
width: 100%;
}
/* Exceptions Textarea */
.exceptions-input {
width: 100%;
min-height: 80px;
padding: var(--spacing-md);
font-size: var(--font-size-sm);
font-family: monospace;
color: var(--color-text-primary);
background: var(--color-bg-tertiary);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
resize: vertical;
}
.exceptions-input:focus {
outline: none;
border-color: var(--color-primary);
}
/* Override browser autofill styling */
.exceptions-input:-webkit-autofill,
.exceptions-input:-webkit-autofill:hover,
.exceptions-input:-webkit-autofill:focus,
.exceptions-input:-webkit-autofill:active {
-webkit-box-shadow: 0 0 0 1000px var(--color-bg-tertiary) inset !important;
-webkit-text-fill-color: var(--color-text-primary) !important;
background-color: var(--color-bg-tertiary) !important;
border-color: var(--color-border) !important;
caret-color: var(--color-text-primary) !important;
}
.input:-webkit-autofill,
.input:-webkit-autofill:hover,
.input:-webkit-autofill:focus,
.input:-webkit-autofill:active {
-webkit-box-shadow: 0 0 0 1000px var(--color-bg-tertiary) inset !important;
-webkit-text-fill-color: var(--color-text-primary) !important;
background-color: var(--color-bg-tertiary) !important;
border-color: var(--color-border) !important;
caret-color: var(--color-text-primary) !important;
}
.exceptions-input.large {
min-height: 120px;
}
/* Proxy Source Card */
.proxy-source-info {
display: flex;
align-items: center;
gap: var(--spacing-md);
padding: var(--spacing-lg);
background: var(--color-bg-tertiary);
border-top: 1px solid var(--color-border);
}
.proxy-source-icon {
width: 40px;
height: 40px;
border-radius: var(--radius-md);
background: var(--color-bg-secondary);
display: flex;
align-items: center;
justify-content: center;
color: var(--color-primary);
}
.proxy-source-icon .icon {
width: 22px;
height: 22px;
}
.proxy-source-details {
flex: 1;
}
.proxy-source-name {
font-weight: var(--font-weight-medium);
}
.proxy-source-url {
font-size: var(--font-size-xs);
color: var(--color-primary);
font-family: monospace;
opacity: 0.8;
word-break: break-all;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 4px;
transition: all var(--transition-fast);
}
.proxy-source-url:hover {
opacity: 1;
text-decoration: underline;
}
.proxy-source-url .icon-external {
width: 14px;
height: 14px;
flex-shrink: 0;
display: inline-block;
vertical-align: middle;
stroke: currentColor;
margin-left: 2px;
}
.proxy-source-updated {
font-size: var(--font-size-xs);
color: var(--color-text-muted);
}
/* About Section */
.about-logos {
display: flex;
justify-content: center;
align-items: center;
gap: 80px;
padding: var(--spacing-xl);
border-bottom: 1px solid var(--color-border);
}
.about-logo-link {
display: flex;
align-items: center;
justify-content: center;
transition: all var(--transition-fast);
}
.about-logo-link:hover {
transform: scale(1.05);
}
.about-logo {
height: 35px;
width: auto;
filter: drop-shadow(0 0 8px rgba(3, 189, 197, 0.3));
}
.about-links {
display: flex;
gap: var(--spacing-md);
padding: var(--spacing-lg);
}
.about-link {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: var(--spacing-xs);
padding: var(--spacing-md);
background: var(--color-bg-tertiary);
border-radius: var(--radius-md);
color: var(--color-text-secondary);
text-decoration: none;
transition: all var(--transition-fast);
}
.about-link .icon {
width: 20px;
height: 20px;
}
.about-link:hover {
background: var(--color-bg-hover);
color: var(--color-primary);
}
.about-link span {
font-size: var(--font-size-xs);
}
.donate-section {
padding: var(--spacing-lg);
text-align: center;
border-top: 1px solid var(--color-border);
}
.donate-message {
font-size: var(--font-size-sm);
color: var(--color-text-muted);
margin-bottom: var(--spacing-md);
line-height: 1.5;
}
.btn-donate {
background: linear-gradient(135deg, var(--color-primary), #e74c3c);
color: white;
border: none;
padding: var(--spacing-md) var(--spacing-xl);
font-size: var(--font-size-base);
font-weight: var(--font-weight-semibold);
text-decoration: none;
}
.btn-donate:hover {
background: linear-gradient(135deg, var(--color-primary-hover), #c0392b);
transform: scale(1.02);
}
.btn-donate .icon {
color: white;
}
.disclaimer {
text-align: center;
padding: var(--spacing-md) var(--spacing-lg);
color: var(--color-text-muted);
font-size: var(--font-size-xs);
border-top: 1px solid var(--color-border);
line-height: 1.5;
}
.disclaimer a {
color: var(--color-primary);
text-decoration: none;
}
.disclaimer a:hover {
text-decoration: underline;
}
.version-info {
text-align: center;
padding: var(--spacing-lg);
color: var(--color-text-muted);
font-size: var(--font-size-sm);
border-top: 1px solid var(--color-border);
}
.version-info .open-source {
margin-top: var(--spacing-sm);
}
.version-info .open-source a {
color: var(--color-primary);
text-decoration: none;
transition: all var(--transition-fast);
}
.version-info .open-source a:hover {
text-decoration: underline;
}
/* Action Buttons */
.action-buttons {
display: flex;
gap: var(--spacing-md);
padding: var(--spacing-lg);
border-top: 1px solid var(--color-border);
}
.action-buttons .btn {
flex: 1;
}
/* Buttons */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--spacing-sm);
padding: var(--spacing-sm) var(--spacing-lg);
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
border-radius: var(--radius-md);
cursor: pointer;
transition: all var(--transition-fast);
}
.btn-primary {
background: var(--color-primary);
color: white;
border: none;
}
.btn-primary:hover {
background: var(--color-primary-hover);
}
.btn-secondary {
background: var(--color-bg-tertiary);
color: var(--color-text-secondary);
border: 1px solid var(--color-border);
}
.btn-secondary:hover {
background: var(--color-bg-hover);
color: var(--color-text-primary);
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
/* Toast Notification */
.toast {
position: fixed;
bottom: var(--spacing-xl);
left: 50%;
transform: translateX(-50%) translateY(100px);
padding: var(--spacing-md) var(--spacing-xl);
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
box-shadow: var(--shadow-lg);
display: flex;
align-items: center;
gap: var(--spacing-md);
opacity: 0;
transition: all var(--transition-normal);
z-index: var(--z-tooltip);
}
.toast.show {
transform: translateX(-50%) translateY(0);
opacity: 1;
}
.toast.success {
border-color: var(--color-success);
}
.toast.success .icon {
color: var(--color-success);
}
.toast.error {
border-color: var(--color-error);
}
.toast.error .icon {
color: var(--color-error);
}
.toast .icon {
width: 18px;
height: 18px;
}
/* Confirmation Modal */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
opacity: 0;
visibility: hidden;
transition: all 0.2s ease;
}
.modal-overlay.show {
opacity: 1;
visibility: visible;
}
.modal {
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
padding: 32px 24px 24px;
max-width: 400px;
width: 90%;
text-align: center;
transform: scale(0.9);
transition: transform 0.2s ease;
}
.modal-overlay.show .modal {
transform: scale(1);
}
.modal-icon {
width: 48px;
height: 48px;
margin: 0 auto var(--spacing-md);
color: var(--color-warning);
}
.modal-icon svg {
width: 100%;
height: 100%;
}
.modal-title {
font-size: var(--font-size-lg);
font-weight: 600;
color: var(--color-text-primary);
margin: 0 0 var(--spacing-sm);
}
.modal-message {
font-size: var(--font-size-sm);
color: var(--color-text-muted);
margin: 0 0 var(--spacing-lg);
line-height: 1.5;
}
.modal-buttons {
display: flex;
gap: var(--spacing-sm);
justify-content: center;
}
.modal-buttons .btn {
min-width: 100px;
}
.btn-danger {
background: var(--color-error);
color: white;
border: none;
}
.btn-danger:hover {
background: #c0392b;
}
/* Bypass List Buttons */
.bypass-buttons {
display: flex;
gap: var(--spacing-sm);
justify-content: flex-end;
margin-top: var(--spacing-sm);
}
.btn-sm {
padding: var(--spacing-sm) var(--spacing-md);
font-size: var(--font-size-sm);
}
.btn-sm .icon {
width: 16px;
height: 16px;
}

493
css/popup.css Normal file
View File

@ -0,0 +1,493 @@
/* ANyONe Extension v2 - Popup Styles */
@import url('common.css');
/* Popup Container */
html {
width: 320px;
height: 600px;
margin: 0;
overflow: hidden;
scrollbar-width: none;
}
html::-webkit-scrollbar {
display: none;
}
body {
width: 320px;
height: 600px;
margin: 0 !important;
padding: 0 !important;
overflow: hidden !important;
scrollbar-width: none;
}
body::-webkit-scrollbar {
display: none;
}
.popup-container {
display: flex;
flex-direction: column;
width: 320px;
height: 600px;
max-height: 600px;
padding: var(--spacing-md);
box-sizing: border-box;
overflow: hidden !important;
}
/* Header */
.popup-header {
text-align: center;
padding: var(--spacing-sm) 0;
margin-bottom: var(--spacing-md);
}
.logo {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: var(--spacing-xs);
}
.logo-img {
height: 32px;
width: auto;
filter: drop-shadow(0 0 12px rgba(3, 189, 197, 0.5));
}
.logo-subtitle {
font-size: var(--font-size-xs);
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: 2px;
}
/* Icons */
.icon {
width: 18px;
height: 18px;
stroke: currentColor;
flex-shrink: 0;
}
.icon-chevron {
width: 16px;
height: 16px;
color: var(--color-text-muted);
transition: transform var(--transition-fast);
}
/* Status Circle */
.status-circle {
display: inline-block;
width: 32px;
height: 32px;
border-radius: 50%;
border: 3px solid var(--color-error);
background: rgba(231, 76, 60, 0.1);
transition: all var(--transition-fast);
}
.status-circle.online {
border-color: var(--color-success);
background: rgba(2, 175, 80, 0.1);
box-shadow: 0 0 12px rgba(2, 175, 80, 0.4);
}
.status-circle.checking {
border-color: var(--color-warning);
background: rgba(243, 156, 18, 0.1);
animation: pulse-status 1.5s ease-in-out infinite;
}
@keyframes pulse-status {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.1); opacity: 0.7; }
}
/* Mode Selection */
.mode-section {
margin-bottom: var(--spacing-lg);
}
/* Mode Content */
.mode-content {
display: none;
}
.mode-content.active {
display: block;
margin-bottom: var(--spacing-sm);
}
.mode-content .card {
height: 140px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
/* Proxy Info Card */
.proxy-info-card {
margin-bottom: var(--spacing-sm);
}
.proxy-info {
margin-bottom: var(--spacing-md);
}
.proxy-info-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--spacing-xs) 0;
}
.proxy-info-label {
font-size: var(--font-size-sm);
color: var(--color-text-muted);
}
.proxy-info-value {
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
color: var(--color-text-primary);
}
.proxy-info-value.muted {
color: var(--color-text-muted);
font-weight: var(--font-weight-normal);
}
.proxy-actions {
display: flex;
gap: var(--spacing-sm);
}
.proxy-actions .btn {
flex: 1;
}
/* Connect Button Section */
.connect-section {
display: flex;
justify-content: center;
padding: var(--spacing-lg) 0 var(--spacing-sm) 0;
}
.connect-btn {
position: relative;
width: 100px;
height: 100px;
border-radius: 50%;
background: var(--color-bg-tertiary);
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all var(--transition-normal);
overflow: visible;
}
.connect-btn-ring {
position: absolute;
inset: -4px;
border-radius: 50%;
border: 3px solid var(--color-border-light);
opacity: 0.6;
transition: all var(--transition-normal);
}
.connect-btn-icon {
width: 100px;
height: 100px;
filter: grayscale(100%) brightness(0.6);
transition: all var(--transition-normal);
}
.connect-btn:hover {
transform: scale(1.05);
box-shadow: 0 0 30px rgba(139, 140, 167, 0.3);
}
.connect-btn:hover .connect-btn-ring {
opacity: 1;
}
/* Connected State */
.connect-btn.connected {
background: rgba(2, 175, 80, 0.1);
}
.connect-btn.connected .connect-btn-ring {
border-color: var(--color-success);
box-shadow: 0 0 20px rgba(2, 175, 80, 0.4);
opacity: 1;
}
.connect-btn.connected .connect-btn-icon {
filter: grayscale(100%) brightness(0.6) sepia(100%) hue-rotate(70deg) saturate(500%);
}
.connect-btn.connected:hover {
box-shadow: 0 0 40px rgba(2, 175, 80, 0.4);
}
/* Connecting State */
.connect-btn.connecting .connect-btn-ring {
border-color: var(--color-warning);
animation: pulse-ring 1.5s ease-in-out infinite;
}
.connect-btn.connecting .connect-btn-icon {
filter: grayscale(100%) brightness(0.6) sepia(100%) hue-rotate(0deg) saturate(500%);
animation: pulse-icon 1.5s ease-in-out infinite;
}
/* Error State */
.connect-btn.error .connect-btn-ring {
border-color: var(--color-error);
box-shadow: 0 0 20px rgba(231, 76, 60, 0.4);
}
.connect-btn.error .connect-btn-icon {
filter: grayscale(100%) brightness(0.5) sepia(100%) hue-rotate(-50deg) saturate(600%);
}
/* Blocked State (Kill Switch) */
.connect-btn.blocked {
background: rgba(231, 76, 60, 0.15);
}
.connect-btn.blocked .connect-btn-ring {
border-color: var(--color-error);
box-shadow: 0 0 20px rgba(231, 76, 60, 0.6);
animation: pulse-blocked 1s ease-in-out infinite;
}
.connect-btn.blocked .connect-btn-icon {
filter: grayscale(100%) brightness(0.5) sepia(100%) hue-rotate(-50deg) saturate(600%);
}
@keyframes pulse-blocked {
0%, 100% {
transform: scale(1);
box-shadow: 0 0 20px rgba(231, 76, 60, 0.6);
}
50% {
transform: scale(1.05);
box-shadow: 0 0 30px rgba(231, 76, 60, 0.8);
}
}
@keyframes pulse-ring {
0%, 100% {
transform: scale(1);
opacity: 0.6;
}
50% {
transform: scale(1.1);
opacity: 1;
}
}
@keyframes pulse-icon {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.1); }
}
/* Status Card */
.status-card {
background: transparent;
padding: var(--spacing-sm);
margin-top: var(--spacing-sm);
margin-bottom: var(--spacing-sm);
height: 100px;
}
.status-main {
display: flex;
align-items: center;
justify-content: center;
}
.status-dot-container {
display: none;
}
.status-dot-container .status-dot {
width: 16px;
height: 16px;
border-radius: 50%;
margin: 0;
transition: all var(--transition-fast);
}
.status-dot-container .status-dot.online {
background: var(--color-success);
box-shadow: 0 0 12px rgba(2, 175, 80, 0.6);
}
.status-dot-container .status-dot.offline {
background: var(--color-error);
box-shadow: 0 0 8px rgba(231, 76, 60, 0.4);
}
.status-dot-container .status-dot.connecting {
background: var(--color-warning);
box-shadow: 0 0 12px rgba(243, 156, 18, 0.6);
animation: pulse-dot 1.5s ease-in-out infinite;
}
.status-dot-container .status-dot.error {
background: var(--color-error);
box-shadow: 0 0 12px rgba(231, 76, 60, 0.6);
}
.status-dot-container .status-dot.blocked {
background: var(--color-error);
box-shadow: 0 0 12px rgba(231, 76, 60, 0.8);
animation: pulse-blocked-dot 1s ease-in-out infinite;
}
@keyframes pulse-blocked-dot {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.3); }
}
@keyframes pulse-dot {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.2); opacity: 0.7; }
}
.status-info {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: 2px;
}
.status-text {
font-size: var(--font-size-lg);
font-weight: var(--font-weight-semibold);
color: var(--color-text-primary);
}
.status-card.error .status-text {
font-size: var(--font-size-lg);
font-weight: var(--font-weight-semibold);
color: var(--color-error);
}
.status-card.connected .status-text {
color: var(--color-success);
}
.status-card.connecting .status-text {
color: var(--color-warning);
}
.status-card.disconnected .status-text {
color: var(--color-text-muted);
}
.status-card.blocked .status-text {
color: var(--color-error);
animation: blink-text 1s ease-in-out infinite;
}
@keyframes blink-text {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.status-ip {
font-size: var(--font-size-sm);
color: var(--color-text-muted);
}
/* Custom Form Labels */
.custom-form .input-label {
color: var(--color-text-muted);
font-weight: normal;
}
/* Quick Actions */
.quick-actions {
display: flex;
gap: var(--spacing-sm);
padding: var(--spacing-sm) 0;
}
.quick-action {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
padding: var(--spacing-sm) var(--spacing-xs);
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
cursor: pointer;
transition: all var(--transition-fast);
}
.quick-action:hover {
background: var(--color-bg-tertiary);
border-color: var(--color-border-light);
}
.quick-action .icon {
width: 18px;
height: 18px;
color: var(--color-text-secondary);
transition: color var(--transition-fast);
}
.quick-action:hover .icon {
color: var(--color-primary);
}
.quick-action .icon.spinning {
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.quick-action-label {
font-size: var(--font-size-xs);
color: var(--color-text-muted);
}
/* Custom Proxy Form */
.custom-form {
display: flex;
flex-direction: column;
gap: var(--spacing-md);
}
.input-row {
display: flex;
gap: var(--spacing-md);
}
.input-row .input-group {
flex: 1;
margin-bottom: 0;
}
.input-row .input-group.port {
flex: 0 0 80px;
}

98
css/variables.css Normal file
View File

@ -0,0 +1,98 @@
/* ANyONe Extension v2 - Design System */
:root {
/* Colors - Primary (ANyONe Teal/Cyan) */
--color-primary: #03BDC5;
--color-primary-light: #2DD4DB;
--color-primary-dark: #0A9BA2;
/* Colors - Secondary (ANyONe Deep Teal) */
--color-secondary: #0D6B7C;
--color-secondary-light: #1A8A9C;
--color-secondary-dark: #084B56;
/* Colors - Accent (ANyONe Blue) */
--color-accent: #0280AF;
--color-accent-light: #0A9FD4;
--color-accent-dark: #065A7A;
/* Colors - Background (Dark Theme) */
--color-bg-primary: #0A1218;
--color-bg-secondary: #0F1A22;
--color-bg-tertiary: #152530;
--color-bg-hover: #1C3040;
/* Colors - Text */
--color-text-primary: #FFFFFF;
--color-text-secondary: #B0C4CC;
--color-text-muted: #6B8A95;
/* Colors - Status */
--color-success: #02AF50;
--color-warning: #F39C12;
--color-error: #E74C3C;
--color-info: #0280AF;
/* Colors - Border */
--color-border: #1E3A47;
--color-border-light: #2A4A58;
/* Spacing */
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 12px;
--spacing-lg: 16px;
--spacing-xl: 24px;
--spacing-2xl: 32px;
/* Border Radius */
--radius-sm: 6px;
--radius-md: 10px;
--radius-lg: 14px;
--radius-xl: 20px;
--radius-full: 9999px;
/* Typography */
--font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, sans-serif;
--font-size-xs: 10px;
--font-size-sm: 12px;
--font-size-md: 14px;
--font-size-lg: 16px;
--font-size-xl: 20px;
--font-size-2xl: 24px;
--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
/* Shadows */
--shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.3);
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4);
--shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.5);
--shadow-glow: 0 0 20px rgba(3, 189, 197, 0.4);
--shadow-glow-success: 0 0 20px rgba(2, 175, 80, 0.4);
/* Transitions */
--transition-fast: 150ms ease;
--transition-normal: 250ms ease;
--transition-slow: 350ms ease;
/* Z-index */
--z-dropdown: 100;
--z-modal: 200;
--z-tooltip: 300;
}
/* Light theme option */
[data-theme="light"] {
--color-bg-primary: #F0F5F7;
--color-bg-secondary: #FFFFFF;
--color-bg-tertiary: #E5EDEF;
--color-bg-hover: #D5E0E5;
--color-text-primary: #0A1218;
--color-text-secondary: #3A5A68;
--color-text-muted: #6B8A95;
--color-border: #C5D5DC;
--color-border-light: #D5E0E5;
}

View File

@ -1,342 +1,323 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<title>Proxy Settings</title>
<style>
* {
box-sizing: border-box;
}
body {
font-family: Arial, Helvetica, sans-serif;
margin: 0;
text-align: center;
color: #ffffff;
background-image: url('../images/optionsback.png');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
background-attachment: fixed;
min-height: 100vh;
overflow-y: hidden;
display: flex;
justify-content: center;
align-items: center;
}
h1 {
margin-top: 20px;
white-space: nowrap;
font-size: 24px;
}
.container {
width: 400px;
margin: 15px auto;
padding: 20px;
background-color: rgba(0, 0, 0, 0.6);
border-radius: 10px;
box-sizing: border-box;
overflow: hidden;
text-align: center;
}
.form-group {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
}
label {
display: inline-block;
margin-bottom: 10px;
margin-top: 20px;
margin-left: 5px;
white-space: nowrap;
color: #ecf0f1;
}
select, input {
width: 170px;
padding: 5px;
margin: 0;
border-radius: 5px;
border: 1px solid rgb(0, 114, 117);
font-size: 14px;
color: #fff;
background-color: rgb(10, 18, 30);
transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
select:focus, input:focus {
outline: none;
border-color: #00ced1;
box-shadow: 0 0 5px rgba(0, 206, 209, 0.5);
}
#proxyPort {
width: 90px;
}
.no-proxy-group {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin: 10px 0;
}
.no-proxy-group label {
margin-bottom: 0;
margin-top: 0;
margin-right: 2px;
}
#noProxyFor {
width: 95%;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
padding: 5px;
margin: 0 auto;
border-radius: 5px;
border: 1px solid rgb(0, 114, 117);
font-size: 14px;
color: #fff;
background-color: rgb(10, 18, 30);
display: block;
max-width: 100%;
}
#noProxyFor::placeholder {
text-align: center;
}
#noProxyFor:focus {
outline: none;
border-color: #00ced1;
box-shadow: 0 0 5px rgba(0, 206, 209, 0.5);
}
button {
padding: 7px 10px;
margin: 10px 0;
border-radius: 5px;
color: #fff;
cursor: pointer;
background-color: transparent;
transition: background-color 0.3s ease, transform 0.2s ease, box-shadow 0.3s ease, filter 0.3s ease;
width: 120px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
#saveSettings {
border: 1px solid rgb(3, 189, 197);
background-color: rgba(3, 189, 197, 0.1);
}
#saveSettings:hover {
background-color: rgb(3, 189, 197);
}
#disableProxy {
width: 80px;
border: 1px solid #a22020;
background-color: rgba(162, 32, 32, 0.3);
}
#disableProxy:hover {
background-color: #bf2626;
}
#checkAnyoneButton {
border: 1px solid #0280AF;
background-color: rgba(2, 128, 175, 0.1);
}
#checkAnyoneButton:hover {
background-color: #0280AF;
filter: brightness(1.2);
}
.container .button-group {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 15px;
}
.credits {
margin-top: 15px;
margin-bottom: 10px;
display: flex;
flex-direction: column;
align-items: center;
}
.credits-link {
text-decoration: none;
color: inherit;
display: flex;
align-items: center;
margin-bottom: 10px;
transition: all 0.3s ease;
}
.credits-link:hover {
opacity: 0.8;
transform: translateY(-2px);
color: #2ecc71;
}
.credits-link span {
font-size: 14px;
transition: color 0.1s ease;
}
.credits img {
margin-right: 10px;
transition: transform 0.3s ease;
}
.credits-link:hover img {
transform: scale(1.1);
}
#anonLogo {
margin: 10px auto 10px;
display: block;
}
#statusMessage {
min-height: 40px;
margin: 10px auto;
padding: 0 10px;
max-width: calc(100% - 20px);
width: 100%;
text-align: center !important;
white-space: normal !important;
word-wrap: break-word !important;
word-break: break-all !important;
overflow: hidden !important;
box-sizing: border-box !important;
font-family: Arial, sans-serif !important;
font-size: 14px;
font-weight: bold !important;
display: flex;
justify-content: center;
align-items: center;
}
@media (min-width: 1200px) {
.form-group, .no-proxy-group {
margin-bottom: 10px;
}
.button-group {
gap: 15px;
}
button {
width: 120px;
}
#disableProxy {
width: 80px;
}
}
@media (max-width: 600px) {
.container {
width: 90%;
padding: 15px;
margin: 10px auto;
}
#statusMessage {
font-size: 12px;
}
select, input, button {
width: 100%;
}
.form-group {
flex-direction: column;
align-items: flex-start;
}
label {
margin-bottom: 5px;
}
.button-group {
flex-direction: row;
flex-wrap: wrap;
gap: 8px;
}
.credits {
align-items: flex-start;
}
.no-proxy-group label {
width: 100%;
}
#noProxyFor {
width: 100%;
}
h1 {
font-size: 20px;
}
}
</style>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ANyONe Extension Settings</title>
<link rel="stylesheet" href="../css/options.css">
</head>
<body>
<div class="container">
<img src="../images/anonlogo.png" alt="Anon Logo" width="120" height="120" id="anonLogo">
<h1>My Socks5 Proxy Settings</h1>
<label for="proxyIP">
Host
<input type="text" id="proxyIP" placeholder="enter host" aria-label="Proxy Host">
</label>
<div class="options-container">
<!-- Header -->
<header class="options-header">
<img src="../images/anonlogo.png" alt="ANyONe" class="header-logo-img">
<h1 class="options-title">Extension Settings</h1>
</header>
<label for="proxyPort">
Port
<input type="number" id="proxyPort" placeholder="port" aria-label="Proxy Port">
<!-- General Settings -->
<section class="settings-section">
<div class="section-title">General</div>
<div class="settings-card">
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Auto-connect on startup</div>
<div class="setting-desc">Automatically connect when browser starts</div>
</div>
<div class="setting-control">
<label class="toggle">
<input type="checkbox" id="auto-connect">
<span class="toggle-slider"></span>
</label>
<div class="no-proxy-group">
<label for="noProxyFor">No Proxy for</label>
<input type="text" id="noProxyFor" placeholder="e.g., localhost, *.example.com" name="noProxyFor" aria-label="No Proxy For">
</div>
</div>
<div class="button-group">
<button id="saveSettings">Save & Enable</button>
<button id="disableProxy">Disable</button>
<button id="checkAnyoneButton">Check ANyONe</button>
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Default connection mode</div>
<div class="setting-desc">Mode to use when auto-connecting</div>
</div>
<p id="statusMessage"></p>
<div class="setting-control">
<select class="select" id="default-mode">
<option value="public">Public Proxies</option>
<option value="custom">Custom Proxy</option>
</select>
</div>
</div>
</div>
</section>
<div class="credits">
<a href="https://debros.io" target="_blank" rel="noopener noreferrer" class="credits-link" aria-label="Visit DeBros website">
<img src="../images/debroslogo.png" alt="DeBros Logo" width="30" height="30">
<span>This extension was created by DeBros</span>
<!-- Privacy Settings -->
<section class="settings-section">
<div class="section-title">Privacy</div>
<div class="settings-card">
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Local Network Access</div>
<div class="setting-desc">Access printers, NAS, and local devices while connected</div>
</div>
<div class="setting-control">
<label class="toggle">
<input type="checkbox" id="bypass-local" checked>
<span class="toggle-slider"></span>
</label>
</div>
</div>
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">WebRTC Leak Protection</div>
<div class="setting-desc">Prevent real IP leaks through WebRTC</div>
</div>
<div class="setting-control">
<label class="toggle">
<input type="checkbox" id="webrtc-protection">
<span class="toggle-slider"></span>
</label>
</div>
</div>
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Kill Switch</div>
<div class="setting-desc">Block all traffic if proxy disconnects</div>
</div>
<div class="setting-control">
<label class="toggle">
<input type="checkbox" id="kill-switch">
<span class="toggle-slider"></span>
</label>
</div>
</div>
<div class="setting-row vertical">
<div class="setting-info" style="width: 100%;">
<div class="setting-label">Bypass List <span class="setting-hint">(comma separated)</span></div>
<div class="setting-desc">Domains and IPs that should bypass the proxy (applies to both Public and Custom modes)</div>
</div>
<div class="setting-control">
<textarea class="exceptions-input large" id="exceptions" placeholder="example.com, *.mydomain.com, 203.0.113.*" autocomplete="off" spellcheck="false"></textarea>
</div>
<div class="bypass-buttons">
<button class="btn btn-secondary btn-sm" id="btn-clear-bypass">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="3 6 5 6 21 6"/>
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
</svg>
Clear
</button>
<button class="btn btn-primary btn-sm" id="btn-save-bypass">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/>
<polyline points="17 21 17 13 7 13 7 21"/>
<polyline points="7 3 7 8 15 8"/>
</svg>
Save
</button>
</div>
</div>
</div>
</section>
<!-- Public Proxies Settings -->
<section class="settings-section">
<div class="section-title">Public Proxies</div>
<div class="settings-card">
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Proxy list source</div>
<div class="setting-desc">Where to fetch the public proxy list from</div>
</div>
<div class="setting-control">
<select class="select" id="proxy-source">
<option value="arweave">Arweave</option>
<option value="git">GitBros</option>
<option value="github">GitHub</option>
</select>
</div>
</div>
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Auto-update interval</div>
<div class="setting-desc">How often to refresh proxy list</div>
</div>
<div class="setting-control">
<select class="select" id="update-interval">
<option value="1">Every hour</option>
<option value="6">Every 6 hours</option>
<option value="12">Every 12 hours</option>
<option value="24">Every 24 hours</option>
<option value="0" selected>Manual only</option>
</select>
</div>
</div>
<div class="proxy-source-info">
<div class="proxy-source-icon">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/>
<polyline points="3.27 6.96 12 12.01 20.73 6.96"/>
<line x1="12" y1="22.08" x2="12" y2="12"/>
</svg>
</div>
<div class="proxy-source-details">
<div class="proxy-source-name" id="source-name">GitBros</div>
<a class="proxy-source-url" id="source-url" href="https://git.debros.io/DeBros/anyone-proxy-list" target="_blank">
git.debros.io/DeBros/anyone-proxy-list
<svg class="icon-external" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
<polyline points="15 3 21 3 21 9"/>
<line x1="10" y1="14" x2="21" y2="3"/>
</svg>
</a>
<a href="https://git.debros.io/DeBros/anyone-extension" target="_blank" rel="noopener noreferrer" class="credits-link" aria-label="Explore the open source code and documentation">
<span>It's open source - explore the code and docs here</span>
<div class="proxy-source-updated" id="source-updated">Last updated: Never</div>
</div>
<button class="btn btn-secondary" id="btn-refresh-proxies">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="23 4 23 10 17 10"/>
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>
</svg>
Refresh
</button>
</div>
</div>
</section>
<!-- Custom Proxy Settings -->
<section class="settings-section">
<div class="section-title">Custom Proxy</div>
<div class="settings-card">
<div class="setting-row vertical">
<div class="setting-info">
<div class="setting-label">Proxy Address</div>
<div class="setting-desc">Your custom SOCKS5 proxy server</div>
</div>
<div class="setting-control">
<div class="port-inputs">
<div class="port-input-group" style="flex: 2;">
<label>IP / Hostname</label>
<input type="text" class="input" id="custom-ip" placeholder="192.168.1.100 or relayup.local" autocomplete="off" spellcheck="false">
</div>
<div class="port-input-group" style="flex: 1;">
<label>Port</label>
<input type="number" class="input" id="custom-port" placeholder="9050" autocomplete="off">
</div>
</div>
</div>
</div>
<div class="setting-row vertical">
<div class="setting-info">
<div class="setting-label">Authentication <span class="setting-hint">(optional)</span></div>
<div class="setting-desc">Username and password if your proxy requires authentication</div>
</div>
<div class="setting-control">
<div class="port-inputs">
<div class="port-input-group">
<label>Username</label>
<input type="text" class="input" id="custom-username" placeholder="username" autocomplete="off" spellcheck="false">
</div>
<div class="port-input-group">
<label>Password</label>
<input type="password" class="input" id="custom-password" placeholder="password" autocomplete="off">
</div>
</div>
</div>
</div>
<div class="action-buttons">
<button class="btn btn-secondary" id="btn-clear-custom">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="3 6 5 6 21 6"/>
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
</svg>
Clear
</button>
<button class="btn btn-secondary" id="btn-test-custom">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
<polyline points="22 4 12 14.01 9 11.01"/>
</svg>
Test Connection
</button>
<button class="btn btn-primary" id="btn-save-custom">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/>
<polyline points="17 21 17 13 7 13 7 21"/>
<polyline points="7 3 7 8 15 8"/>
</svg>
Save Settings
</button>
</div>
</div>
</section>
<!-- About Section -->
<section class="settings-section">
<div class="section-title">About</div>
<div class="settings-card">
<div class="about-logos">
<a href="https://anyone.io" target="_blank" class="about-logo-link">
<img src="../images/anyone2.png" alt="ANyONe" class="about-logo">
</a>
<a href="https://debros.io" target="_blank" class="about-logo-link">
<img src="../images/DeBros_White_Transparent.png" alt="DeBros" class="about-logo">
</a>
</div>
<div class="donate-section">
<p class="donate-message">DeBros creates privacy-first and decentralized tools for a freer world. Support our mission.</p>
<a href="https://debros.io/donate" target="_blank" class="btn btn-donate">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/>
</svg>
Support DeBros
</a>
</div>
<script src="../js/options.js"></script>
<div class="disclaimer">
This software is provided "as is" without warranty. Use at your own risk.
See <a href="https://github.com/DeBrosOfficial/anyone-extension/blob/main/LICENSE" target="_blank">LICENSE</a> for details.
</div>
<div class="version-info">
<div>Created by DeBros | Version 2.0.0</div>
<div class="open-source">
<a href="https://git.debros.io/DeBros/anyone-extension" target="_blank">View Source Code</a>
</div>
</div>
</div>
</section>
</div>
<!-- Confirmation Modal -->
<div class="modal-overlay" id="modal-overlay">
<div class="modal">
<div class="modal-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>
<line x1="12" y1="9" x2="12" y2="13" stroke-width="2.5"/>
<circle cx="12" cy="17" r="1" fill="currentColor" stroke="none"/>
</svg>
</div>
<h3 class="modal-title" id="modal-title">Disable Local Network Access?</h3>
<p class="modal-message" id="modal-message">
This will make local devices (printers, NAS, router etc) unreachable while connected to the proxy.
</p>
<div class="modal-buttons">
<button class="btn btn-secondary" id="modal-cancel">Cancel</button>
<button class="btn btn-danger" id="modal-confirm">Disable</button>
</div>
</div>
</div>
<!-- Toast Notification -->
<div class="toast" id="toast">
<svg class="icon" id="toast-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
<polyline points="22 4 12 14.01 9 11.01"/>
</svg>
<span id="toast-message">Settings saved</span>
</div>
<script type="module" src="../js/options.js"></script>
</body>
</html>

View File

@ -1,225 +1,125 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<title>ANyONe Network Gateway</title>
<style>
body {
font-family: Arial, Helvetica, sans-serif;
margin: 0;
padding: 10px 30px 10px;
background-image: url('../images/popupback.png');
background-size: cover;
background-repeat: no-repeat;
background-position: center;
background-color: #6d8392;
color: white;
text-align: center;
width: 220px;
height: 500px;
overflow: hidden;
}
#anonLogo {
margin: 10px auto 10px;
display: block;
}
.switch {
position: relative;
display: inline-block;
width: 50px;
height: 15px;
}
.switch input {
opacity: 1;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #e74c3c;
transition: 0.4s;
border-radius: 34px;
}
.slider:before {
position: absolute;
content: "";
height: 25px;
width: 25px;
left: 0px;
bottom: -4px;
background-color: #ffffff;
transition: 0.4s;
border-radius: 50%;
}
input:checked + .slider {
background-color: #02af50;
}
input:checked + .slider:before {
transform: translateX(26px);
}
#statusMessage {
margin-top: 30px;
font-size: 16px;
font-weight: bold;
min-height: 30px;
max-width: 100%;
overflow-wrap: break-word;
}
.button-container-1 {
margin-top: 10px;
margin-bottom: 5px;
display: flex;
justify-content: center;
flex-wrap: nowrap;
align-items: center;
position: absolute;
bottom: 140px;
left: 0;
right: 0;
}
.button-container-2 {
margin-top: 10px;
margin-bottom: 20px;
display: flex;
justify-content: center;
flex-wrap: nowrap;
align-items: center;
position: absolute;
bottom: 65px;
left: 0;
right: 0;
}
button {
color: white;
padding: 5px 15px;
font-size: 14px;
font-weight: normal;
border-radius: 5px;
cursor: pointer;
margin: 0 5px;
width: 100px;
height: auto;
box-sizing: border-box;
text-align: center;
white-space: normal;
overflow: visible;
display: inline-flex;
flex-direction: column;
justify-content: center;
}
#checkAnyoneButton {
background-color: #0280AF;
border: 1px solid #0280AF;
}
#checkAnyoneButton:hover {
background-color: #0074f0;
}
#updateProxiesButton {
background-color: #0a121e;
border: 1px solid #0280AF;
}
#updateProxiesButton:hover {
background-color: #0280AF;
}
#optionsButton {
background-color: #0a121e;
border: 1px solid #03bdc5;
}
#optionsButton:hover {
background-color: #03bdc5;
}
#dappStoreButton {
background-color: #0a121e;
border: 1px solid #03bdc5;
}
#dappStoreButton:hover {
background-color: #03bdc5;
}
.footer {
position: absolute;
bottom: 0px;
left: 0;
right: 0;
display: flex;
justify-content: center;
gap: 30px;
}
.social-link {
display: block;
width: 30px;
height: 30px;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
margin-top: 10px;
margin-bottom: 30px;
transition: transform 0.3s ease-in-out, filter 0.3s ease-in-out;
}
.social-link:hover {
transform: scale(1.2) translateY(-1px);
filter: brightness(1.1);
}
.social-link.debros { background-image: url('../images/debroslogo.png'); }
.social-link.x { background-image: url('../images/x.png'); }
.social-link.docs { background-image: url('../images/gitbros.png'); }
.social-link.anyone { background-image: url('../images/anonlogo.png'); }
</style>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ANyONe Extension</title>
<link rel="stylesheet" href="../css/popup.css">
</head>
<body>
<img src="../images/anonlogo.png" alt="AnON Logo" width="120" height="120" id="anonLogo">
<h1>ANyONe Proxy</h1>
<div class="popup-container">
<!-- Header -->
<header class="popup-header">
<div class="logo">
<img src="../images/anyone2.png" alt="ANyONe" class="logo-img">
<span class="logo-subtitle">Privacy Extension</span>
</div>
</header>
<label class="switch">
<input type="checkbox" id="proxyToggle">
<span class="slider"></span>
</label>
<!-- Mode Selection -->
<section class="mode-section">
<div class="mode-tabs">
<button class="mode-tab active" data-mode="public">Public Proxies</button>
<button class="mode-tab" data-mode="custom">Custom Proxy</button>
</div>
</section>
<p id="statusMessage"></p>
<!-- Mode Content: Public -->
<section class="mode-content active" id="mode-public">
<div class="card">
<div class="proxy-info">
<div class="proxy-info-row">
<span class="proxy-info-label">Mode</span>
<span class="proxy-info-value" id="proxy-mode">Auto (Fastest)</span>
</div>
<div class="proxy-info-row">
<span class="proxy-info-label">Available</span>
<span class="proxy-info-value" id="proxy-count">Loading...</span>
</div>
</div>
<div class="proxy-actions">
<button class="btn btn-secondary" id="btn-next-proxy">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="9 18 15 12 9 6"/>
</svg>
Next Proxy
</button>
</div>
</div>
</section>
<div class="button-container-1">
<button id="checkAnyoneButton">Check ANyONe</button>
<button id="updateProxiesButton">Update Proxies</button>
<!-- Mode Content: Custom -->
<section class="mode-content" id="mode-custom">
<div class="card">
<div class="custom-form">
<div class="input-row">
<div class="input-group">
<label class="input-label">IP / Hostname</label>
<input type="text" class="input" id="custom-ip" placeholder="relayup.local">
</div>
<div class="input-group port">
<label class="input-label">Port</label>
<input type="number" class="input" id="custom-port" placeholder="9050">
</div>
</div>
<button class="btn btn-secondary w-full" id="btn-test-custom">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
<polyline points="22 4 12 14.01 9 11.01"/>
</svg>
Test Connection
</button>
</div>
</div>
</section>
<!-- Connect Button -->
<section class="connect-section">
<button class="connect-btn" id="btn-connect">
<div class="connect-btn-ring"></div>
<img src="../images/anonlogo.png" alt="Connect" class="connect-btn-icon">
</button>
</section>
<!-- Status Card -->
<section class="status-card" id="status-card">
<div class="status-main">
<div class="status-dot-container">
<span class="status-dot offline" id="status-dot"></span>
</div>
<div class="status-info">
<span class="status-text" id="status-text">Disconnected</span>
<span class="status-ip" id="status-ip" style="display: none;">-</span>
</div>
</div>
</section>
<!-- Quick Actions -->
<section class="quick-actions">
<button class="quick-action" id="btn-check-ip">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="11" cy="11" r="8"/>
<line x1="21" y1="21" x2="16.65" y2="16.65"/>
</svg>
<span class="quick-action-label">Check IP</span>
</button>
<button class="quick-action" id="btn-refresh">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="23 4 23 10 17 10"/>
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>
</svg>
<span class="quick-action-label">Refresh</span>
</button>
<button class="quick-action" id="btn-settings">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="3"/>
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>
</svg>
<span class="quick-action-label">Settings</span>
</button>
</section>
</div>
<div class="button-container-2">
<button id="optionsButton">Custom Settings</button>
<button id="dappStoreButton">dApp Store</button>
</div>
<div class="footer">
<a href="https://debros.io" class="social-link debros" target="_blank"></a>
<a href="https://x.com/debrosofficial" class="social-link x" target="_blank"></a>
<a href="https://git.debros.io/DeBros/anyone-extension" class="social-link docs" target="_blank"></a>
<a href="https://anyone.io" class="social-link anyone" target="_blank"></a>
</div>
<script src="../js/popup.js"></script>
<script type="module" src="../js/popup.js"></script>
</body>
</html>

View File

@ -1,46 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>dApp Store</title>
<style>
body, html {
height: 100%;
margin: 0;
padding: 0;
}
body {
display: flex;
justify-content: center;
align-items: center;
background-color: black;
font-family: Arial, Helvetica, sans-serif;
}
#fullScreenImage {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
z-index: -1;
opacity: 0;
transition: opacity 2s ease-in-out;
}
#comingSoon {
font-size: 3em;
color: white;
opacity: 0;
transition: opacity 2s ease-in-out;
text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
}
</style>
</head>
<body>
<img id="fullScreenImage" src="../images/comingsoon.png" alt="Coming Soon Background">
<div id="comingSoon">Coming Soon</div>
<script src="../js/store.js"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
images/anyone2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 975 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View File

@ -1,286 +1,728 @@
let proxies = []; // Initially empty, will be populated from local storage or fetched from the server
let currentProxyIndex = 0; // Start with the first proxy
let proxyEnabled = false;
/* ANyONe Extension v2 - Background Service Worker */
console.log("background.js is running");
// ES Module imports
import { CONFIG } from './config.js';
import { Utils } from './utils.js';
import { Storage } from './storage.js';
import { ProxyManager } from './proxy-manager.js';
function applyProxySettings(host, port, exceptions = []) {
const proxyConfig = {
mode: "fixed_servers",
rules: {
singleProxy: {
scheme: "socks5",
host,
port,
},
bypassList: exceptions.concat([""]),
},
};
// ============================================
// Initialization
// ============================================
chrome.proxy.settings.set({ value: proxyConfig, scope: "regular" }, () => {
const lastError = chrome.runtime.lastError;
if (lastError) {
console.error("Error applying proxy settings:", lastError);
fallbackToNextProxy(); // Move to the next proxy if the current one fails
} else {
console.log(`Proxy applied: ${host}:${port}`);
proxyEnabled = true;
}
Utils.log('info', 'Background service worker starting...');
// Initialize on startup
chrome.runtime.onInstalled.addListener(async (details) => {
Utils.log('info', `Extension ${details.reason}`, { version: CONFIG.VERSION });
if (details.reason === 'install') {
// First install
Utils.log('info', 'First install, setting up defaults...');
// Set defaults
await Storage.set({
[CONFIG.STORAGE_KEYS.MODE]: CONFIG.DEFAULTS.MODE,
[CONFIG.STORAGE_KEYS.PROXY_ENABLED]: false,
[CONFIG.STORAGE_KEYS.AUTO_CONNECT]: CONFIG.DEFAULTS.AUTO_CONNECT,
[CONFIG.STORAGE_KEYS.WEBRTC_PROTECTION]: CONFIG.DEFAULTS.WEBRTC_PROTECTION,
[CONFIG.STORAGE_KEYS.BYPASS_LOCAL]: CONFIG.DEFAULTS.BYPASS_LOCAL
});
}
function clearProxySettings() {
chrome.proxy.settings.clear({}, () => {
const lastError = chrome.runtime.lastError;
if (lastError) {
console.error("Error clearing proxy settings:", lastError);
} else {
console.log("Proxy settings cleared.");
proxyEnabled = false;
currentProxyIndex = 0; // Reset to the first proxy
// Auto-fetch proxies on first install
Utils.log('info', 'Fetching initial proxy list...');
try {
const source = CONFIG.DEFAULTS.PROXY_SOURCE;
await handleFetchProxies(source);
Utils.log('info', 'Initial proxy list fetched successfully');
} catch (error) {
Utils.log('error', 'Failed to fetch initial proxy list', error);
}
});
}
function fallbackToNextProxy() {
currentProxyIndex = (currentProxyIndex + 1) % proxies.length; // Move to the next proxy in the list
if (proxies.length > 0) {
const { host, port } = proxies[currentProxyIndex];
console.log(`Falling back to proxy: ${host}:${port}`);
applyProxySettings(host, port);
} else {
console.error("No proxies available to fall back to.");
}
}
function checkProxyFunctionality(proxy, callback) {
const proxyConfig = {
mode: "fixed_servers",
rules: {
singleProxy: {
scheme: "socks5",
host: proxy.host,
port: proxy.port
},
bypassList: [""]
}
};
chrome.proxy.settings.set({ value: proxyConfig, scope: "regular" }, () => {
if (chrome.runtime.lastError) {
console.error("Error setting proxy for check:", chrome.runtime.lastError);
callback(false);
return;
}
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout
fetch('https://check.en.anyone.tech', {
mode: 'no-cors',
signal: controller.signal
})
.then(response => {
clearTimeout(timeoutId);
if (response.ok) {
console.log("Proxy check successful");
callback(true); // Connection successful
} else {
console.log("Proxy check failed");
callback(false); // Connection failed
}
})
.catch(error => {
if (error.name === 'AbortError') {
console.log("Request timed out");
} else {
console.error("Error in proxy check:", error);
}
callback(false);
});
});
}
function enableFirstWorkingProxy(callback) {
let index = 0;
function tryNextProxy() {
if (index >= proxies.length) {
console.log("No working proxy found");
callback(false);
return;
}
const proxy = proxies[index];
console.log("Checking proxy:", proxy.host + ":" + proxy.port);
checkProxyFunctionality(proxy, (isWorking) => {
if (isWorking) {
currentProxyIndex = index; // Update currentProxyIndex
console.log("Found working proxy:", proxy.host + ":" + proxy.port);
applyProxySettings(proxy.host, proxy.port);
chrome.storage.local.set({ proxyEnabled: true, currentProxy: proxy });
callback(true);
} else {
index++;
tryNextProxy();
}
});
}
tryNextProxy();
}
function fetchProxies() {
console.log("Starting to fetch proxies...");
return fetch('https://git.debros.io/DeBros/anyone-proxy-list/raw/branch/main/anonproxies.json') // list of public proxies
.then(response => {
console.log("Response status:", response.status);
if (!response.ok) {
console.error("Response not OK");
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
console.log("Data received:", data);
proxies = data;
// Save the proxies to local storage
chrome.storage.local.set({ 'proxyList': proxies }, () => {
console.log('Proxies updated and saved:', proxies);
});
})
.catch(error => {
console.error('Failed to fetch proxies:', error);
});
}
// Load proxies from storage on extension startup if they exist
chrome.storage.local.get('proxyList', function(result) {
if (result.proxyList) {
proxies = result.proxyList;
console.log('Loaded proxies from storage:', proxies);
} else {
console.log('No proxies found in local storage, fetch required.');
} else if (details.reason === 'update') {
Utils.log('info', 'Extension updated');
}
});
// This event listener runs when the extension is first installed or updated
chrome.runtime.onInstalled.addListener((details) => {
if (details.reason === "install") {
console.log("Extension installed for the first time. Fetching proxies...");
// Fetch proxies automatically on first install but don't enable them
fetchProxies().then(() => {
console.log("Proxies updated on first install.");
// Don't enable proxy automatically, just fetch and store it
});
// Load state on startup
chrome.runtime.onStartup.addListener(async () => {
Utils.log('info', 'Browser startup');
await ProxyManager.init();
// Check auto-connect setting
const autoConnect = await Storage.getValue(CONFIG.STORAGE_KEYS.AUTO_CONNECT, false);
if (autoConnect) {
Utils.log('info', 'Auto-connect enabled, connecting...');
const mode = await Storage.getMode();
await handleConnect(mode);
}
});
// ============================================
// Message Handlers
// ============================================
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
console.log("Received message in background:", message);
if (message.action === "enableProxy") {
console.log("Enabling proxy...");
chrome.runtime.sendMessage({ action: "showLoadingMessage", message: "Please wait..." });
enableFirstWorkingProxy((success) => {
if (success) {
console.log("Proxy enabled successfully");
sendResponse({ status: "enabled", proxy: proxies[currentProxyIndex] });
Utils.log('debug', 'Message received', message);
// Handle async responses
handleMessage(message, sender)
.then(response => sendResponse(response))
.catch(error => {
Utils.log('error', 'Message handler error', error);
sendResponse({ success: false, error: error.message });
});
return true; // Keep channel open for async response
});
/**
* Handle incoming messages
* @param {object} message - Message object
* @param {object} sender - Sender info
* @returns {Promise<object>}
*/
async function handleMessage(message, sender) {
switch (message.action) {
// ============================================
// Connection Actions
// ============================================
case 'connect':
return handleConnect(message.mode, message.options);
case 'disconnect':
return handleDisconnect();
case 'getStatus':
return handleGetStatus();
// ============================================
// Proxy List Actions
// ============================================
case 'fetchProxies':
return handleFetchProxies(message.source);
case 'getProxyList':
return handleGetProxyList();
case 'testProxy':
return handleTestProxy(message.proxy);
// ============================================
// Settings Actions
// ============================================
case 'getSettings':
return handleGetSettings();
case 'saveSettings':
return handleSaveSettings(message.settings);
case 'clearCustomProxy':
return handleClearCustomProxy();
case 'setMode':
return handleSetMode(message.mode);
case 'nextProxy':
return handleNextProxy();
// ============================================
// Utility Actions
// ============================================
case 'openOptions':
chrome.runtime.openOptionsPage();
return { success: true };
case 'openUrl':
chrome.tabs.create({ url: message.url });
return { success: true };
default:
Utils.log('warn', 'Unknown action', message.action);
return { success: false, error: 'Unknown action' };
}
}
// ============================================
// Action Handlers
// ============================================
/**
* Handle connect action
* @param {string} mode - Connection mode
* @param {object} options - Additional options
* @returns {Promise<object>}
*/
async function handleConnect(mode, options = {}) {
Utils.log('info', 'Connecting', { mode, options });
// Deactivate kill switch if it was active
const killSwitchActive = await Storage.getValue('killSwitchActive', false);
if (killSwitchActive) {
await applyKillSwitch(false);
Utils.log('info', 'Kill switch deactivated for new connection');
}
// Notify popup of loading state
broadcastMessage({ action: 'statusUpdate', status: 'connecting' });
// Get local network access setting and exceptions (bypass list)
const bypassLocal = await Storage.getValue(CONFIG.STORAGE_KEYS.BYPASS_LOCAL, true);
const exceptions = await Storage.getValue(CONFIG.STORAGE_KEYS.EXCEPTIONS, []);
let result;
switch (mode) {
case CONFIG.MODES.PUBLIC:
await ProxyManager.init();
result = await ProxyManager.connectToFastest(options.country, bypassLocal, exceptions);
break;
case CONFIG.MODES.CUSTOM:
const customProxy = await Storage.getCustomProxy();
Utils.log('info', 'Custom proxy settings loaded', {
ip: customProxy.ip,
port: customProxy.port,
exceptions: customProxy.exceptions,
bypassLocal
});
if (!customProxy.ip || !customProxy.port) {
result = { success: false, error: 'Custom proxy not configured' };
} else {
console.log("Failed to enable proxy");
sendResponse({ status: "error", message: "No public proxy available at this moment. Please configure a custom proxy in the settings." });
result = await ProxyManager.connectCustom(
customProxy.ip,
customProxy.port,
customProxy.exceptions,
bypassLocal
);
}
});
return true; // For async response
} else if (message.action === "disableProxy") {
console.log("Disabling proxy...");
clearProxySettings();
chrome.storage.local.set({ proxyEnabled: false, proxyType: null });
sendResponse({ status: "disabled" });
chrome.tabs.query({}, function(tabs) {
for (let tab of tabs) {
chrome.tabs.sendMessage(tab.id, {action: "disableProxy"}, function(response) {
if (chrome.runtime.lastError) {
console.warn("Warning: Could not send message to tab " + tab.id + ". Tab might have been closed.", chrome.runtime.lastError.message);
break;
default:
result = { success: false, error: 'Invalid mode' };
}
if (result.success) {
await Storage.setMode(mode);
await Storage.setProxyEnabled(true);
broadcastMessage({
action: 'statusUpdate',
status: 'connected',
proxy: result.proxy,
mode
});
}
});
} else if (message.action === "updateProxy") {
console.log("Updating proxy settings...");
if (message.type === "custom") {
// Custom proxy enable
applyProxySettings(message.proxy.host, parseInt(message.proxy.port), message.exceptions || []);
chrome.storage.local.set({
proxyEnabled: true,
proxyType: "custom",
proxyIP: message.proxy.host,
proxyPort: message.proxy.port
});
sendResponse({ status: "enabled", proxy: message.proxy });
chrome.tabs.query({}, function(tabs) {
for (let tab of tabs) {
chrome.tabs.sendMessage(tab.id, {action: "updatePopupState"}, function(response) {
if (chrome.runtime.lastError) {
console.warn("Warning: Could not send message to tab " + tab.id + ". Tab might have been closed.", chrome.runtime.lastError.message);
}
});
}
});
} else if (message.type === "public") {
// Public proxy enable
chrome.runtime.sendMessage({ action: "showLoadingMessage", message: "Please wait..." });
enableFirstWorkingProxy((success) => {
if (success) {
console.log("Public proxy enabled successfully");
sendResponse({ status: "enabled", proxy: proxies[currentProxyIndex] });
} else {
console.log("Failed to enable public proxy");
sendResponse({ status: "error", message: "No public proxy available at this moment. Please configure a custom proxy in the settings." });
}
});
return true; // For async response
} else if (message.type === "disabled") {
clearProxySettings();
chrome.storage.local.set({ proxyEnabled: false, proxyType: null });
sendResponse({ status: "disabled" });
chrome.tabs.query({}, function(tabs) {
for (let tab of tabs) {
chrome.tabs.sendMessage(tab.id, {action: "disableProxy"}, function(response) {
if (chrome.runtime.lastError) {
console.warn("Warning: Could not send message to tab " + tab.id + ". Tab might have been closed.", chrome.runtime.lastError.message);
}
broadcastMessage({
action: 'statusUpdate',
status: 'error',
error: result.error
});
}
});
}
} else if (message.action === "proxyFailed") {
console.log("Proxy setup failed:", message.error);
clearProxySettings();
chrome.storage.local.set({ proxyEnabled: false, proxyType: null });
chrome.tabs.query({}, function(tabs) {
for (let tab of tabs) {
chrome.tabs.sendMessage(tab.id, {action: "toggleOff"}, function(response) {
if (chrome.runtime.lastError) {
console.warn("Warning: Could not send message to tab " + tab.id + ". Tab might have been closed.", chrome.runtime.lastError.message);
return result;
}
/**
* Handle disconnect action
* @returns {Promise<object>}
*/
async function handleDisconnect() {
Utils.log('info', 'Disconnecting');
// Check if kill switch should be activated
const killSwitch = await Storage.getValue(CONFIG.STORAGE_KEYS.KILL_SWITCH, false);
const success = await ProxyManager.disconnect();
// If kill switch is enabled, block traffic after disconnect
if (killSwitch) {
await applyKillSwitch(true);
broadcastMessage({
action: 'statusUpdate',
status: 'blocked',
message: 'Kill Switch active - all traffic blocked'
});
}
});
} else if (message.action === "updateProxies") {
console.log("Attempting to update proxies...");
fetchProxies().then(() => {
console.log("Proxies fetched successfully");
sendResponse({success: true, proxies: proxies});
chrome.runtime.sendMessage({ action: "updateStatus", message: "Proxy list updated successfully!", color: "#2ecc71" });
}).catch(error => {
console.error("Failed to fetch proxies:", error);
sendResponse({success: false, message: "Failed to update proxy list."});
chrome.runtime.sendMessage({ action: "updateStatus", message: "Failed to update proxy list.", color: "#e74c3c" });
});
return true; // Indicates this is an async response
} else {
console.log("Unknown action received:", message.action);
sendResponse({ status: "error", message: "Unknown action." });
broadcastMessage({
action: 'statusUpdate',
status: success ? 'disconnected' : 'error'
});
}
await Storage.setProxyEnabled(false);
return { success };
}
/**
* Handle get status action
* @returns {Promise<object>}
*/
async function handleGetStatus() {
const [enabled, mode, currentProxy, killSwitchActive] = await Promise.all([
Storage.isProxyEnabled(),
Storage.getMode(),
Storage.getCurrentProxy(),
Storage.getValue('killSwitchActive', false)
]);
return {
success: true,
enabled,
mode,
currentProxy,
killSwitchActive
};
}
/**
* Handle next proxy action (load balancing)
* @returns {Promise<object>}
*/
async function handleNextProxy() {
Utils.log('info', 'Switching to next proxy');
await ProxyManager.init();
// Check if proxy list is empty and fetch if needed
const status = ProxyManager.getStatus();
if (status.proxyCount === 0) {
Utils.log('info', 'Proxy list empty, fetching first...');
const source = await Storage.getValue(CONFIG.STORAGE_KEYS.PROXY_SOURCE, 'git');
const fetchResult = await ProxyManager.fetchProxyList(source);
if (!fetchResult.success || fetchResult.proxies.length === 0) {
return { success: false, error: 'Failed to fetch proxy list' };
}
}
const result = await ProxyManager.fallbackToNext();
if (result.success) {
broadcastMessage({
action: 'statusUpdate',
status: 'connected',
proxy: result.proxy
});
}
return result;
}
/**
* Handle fetch proxies action
* @param {string} source - Proxy source
* @returns {Promise<object>}
*/
async function handleFetchProxies(source = 'arweave') {
const result = await ProxyManager.fetchProxyList(source);
if (result.success) {
broadcastMessage({
action: 'proxiesUpdated',
count: result.proxies.length,
usedSource: result.usedSource
});
}
return result;
}
/**
* Handle get proxy list action
* @returns {Promise<object>}
*/
async function handleGetProxyList() {
const proxyList = await Storage.getProxyList();
const lastUpdate = await Storage.getLastUpdate();
return {
success: true,
proxies: proxyList,
lastUpdate,
count: proxyList.length
};
}
/**
* Handle test proxy action
* @param {object} proxy - Proxy to test
* @returns {Promise<object>}
*/
async function handleTestProxy(proxy) {
const result = await ProxyManager.testProxy(proxy);
return { success: true, ...result };
}
/**
* Handle get settings action
* @returns {Promise<object>}
*/
async function handleGetSettings() {
const settings = await Storage.getAllSettings();
return { success: true, settings };
}
/**
* Handle save settings action
* @param {object} settings - Settings to save
* @returns {Promise<object>}
*/
async function handleSaveSettings(settings) {
try {
await Storage.set(settings);
// Apply WebRTC protection if changed
if (settings.webrtcProtection !== undefined) {
await applyWebRTCProtection(settings.webrtcProtection);
}
// Apply Kill Switch if changed
if (settings.killSwitch !== undefined) {
const proxyEnabled = await Storage.isProxyEnabled();
if (settings.killSwitch && !proxyEnabled) {
// Kill switch enabled while not connected - block traffic
await applyKillSwitch(true);
broadcastMessage({
action: 'statusUpdate',
status: 'blocked',
message: 'Kill Switch active - connect to unblock'
});
} else if (!settings.killSwitch) {
// Kill switch disabled - restore normal traffic
const killSwitchActive = await Storage.getValue('killSwitchActive', false);
if (killSwitchActive) {
await applyKillSwitch(false);
broadcastMessage({
action: 'statusUpdate',
status: 'disconnected'
});
}
}
}
// Check if custom proxy connection settings changed (IP, port, credentials)
const customProxyConnectionChanged =
settings.proxyIP !== undefined ||
settings.proxyPort !== undefined ||
settings.proxyUsername !== undefined ||
settings.proxyPassword !== undefined;
// Check if bypass settings changed (applies to both public and custom modes)
const bypassSettingsChanged =
settings.bypassLocal !== undefined ||
settings.noProxyFor !== undefined;
// Handle custom proxy connection changes (only affects custom mode)
if (customProxyConnectionChanged) {
const proxyEnabled = await Storage.isProxyEnabled();
const mode = await Storage.getMode();
if (proxyEnabled && mode === CONFIG.MODES.CUSTOM) {
Utils.log('info', 'Custom proxy settings changed while connected, re-applying...');
// Get the updated custom proxy settings
const customProxy = await Storage.getCustomProxy();
const bypassLocal = await Storage.getValue(CONFIG.STORAGE_KEYS.BYPASS_LOCAL, true);
const exceptions = await Storage.getValue(CONFIG.STORAGE_KEYS.EXCEPTIONS, []);
// Validate the new settings
if (!customProxy.ip || !customProxy.port) {
// Invalid settings - disconnect
Utils.log('warn', 'Custom proxy settings invalid, disconnecting...');
await ProxyManager.disconnect();
await Storage.setProxyEnabled(false);
broadcastMessage({
action: 'statusUpdate',
status: 'error',
error: 'Custom proxy not configured'
});
} else {
// Try to apply the new proxy settings
const result = await ProxyManager.applyProxy(
customProxy.ip,
customProxy.port,
exceptions,
bypassLocal
);
if (result) {
broadcastMessage({
action: 'statusUpdate',
status: 'connected',
proxy: { host: customProxy.ip, port: customProxy.port },
mode: CONFIG.MODES.CUSTOM
});
} else {
// Failed to apply - disconnect
await ProxyManager.disconnect();
await Storage.setProxyEnabled(false);
broadcastMessage({
action: 'statusUpdate',
status: 'error',
error: 'Failed to apply proxy settings'
});
}
}
}
}
// Handle bypass settings changes (affects both public and custom modes)
// Skip if custom proxy connection was already re-applied above
if (bypassSettingsChanged && !customProxyConnectionChanged) {
const proxyEnabled = await Storage.isProxyEnabled();
if (proxyEnabled) {
const mode = await Storage.getMode();
const currentProxy = await Storage.getCurrentProxy();
if (currentProxy) {
const bypassLocal = await Storage.getValue(CONFIG.STORAGE_KEYS.BYPASS_LOCAL, true);
const exceptions = await Storage.getValue(CONFIG.STORAGE_KEYS.EXCEPTIONS, []);
Utils.log('info', 'Bypass settings changed, re-applying proxy settings', { bypassLocal, exceptions });
if (mode === CONFIG.MODES.CUSTOM) {
const customProxy = await Storage.getCustomProxy();
await ProxyManager.applyProxy(
customProxy.ip,
customProxy.port,
exceptions,
bypassLocal
);
} else {
await ProxyManager.applyProxy(
currentProxy.host,
currentProxy.port,
exceptions,
bypassLocal
);
}
}
}
}
return { success: true };
} catch (error) {
return { success: false, error: error.message };
}
}
/**
* Handle clear custom proxy action
* @returns {Promise<object>}
*/
async function handleClearCustomProxy() {
try {
Utils.log('info', 'Clearing custom proxy settings...');
await Storage.clearCustomProxy();
Utils.log('info', 'Custom proxy settings cleared successfully');
return { success: true };
} catch (error) {
Utils.log('error', 'Failed to clear custom proxy settings', error);
return { success: false, error: error.message };
}
}
/**
* Handle set mode action
* @param {string} mode - Mode to set
* @returns {Promise<object>}
*/
async function handleSetMode(mode) {
await Storage.setMode(mode);
return { success: true, mode };
}
// ============================================
// Utility Functions
// ============================================
/**
* Broadcast message to all extension pages
* @param {object} message - Message to broadcast
*/
function broadcastMessage(message) {
chrome.runtime.sendMessage(message).catch(() => {
// Ignore errors when no listeners
});
}
/**
* Handle proxy errors
*/
let lastProxyErrorTime = 0;
const PROXY_ERROR_DEBOUNCE_MS = 5000; // Prevent multiple errors within 5 seconds
chrome.proxy.onProxyError.addListener(async (details) => {
// Debounce rapid proxy errors (e.g., when network goes down)
const now = Date.now();
if (now - lastProxyErrorTime < PROXY_ERROR_DEBOUNCE_MS) {
Utils.log('debug', 'Proxy error debounced', details);
return;
}
lastProxyErrorTime = now;
Utils.log('error', 'Proxy error', details);
// Check current mode - only attempt fallback for public proxies
const mode = await Storage.getMode();
const killSwitch = await Storage.getValue(CONFIG.STORAGE_KEYS.KILL_SWITCH, false);
// For non-fatal errors in public mode, attempt fallback
if (!details.fatal && mode === CONFIG.MODES.PUBLIC) {
const result = await ProxyManager.fallbackToNext();
if (result.success) {
broadcastMessage({
action: 'statusUpdate',
status: 'connected',
proxy: result.proxy,
message: 'Switched to backup proxy'
});
return; // Successfully switched, no error to report
}
}
// Fallback failed or fatal error or custom mode - handle disconnect
if (killSwitch) {
// Kill switch is ON - block all traffic
await applyKillSwitch(true);
await Storage.setProxyEnabled(false);
broadcastMessage({
action: 'statusUpdate',
status: 'blocked',
error: details.fatal ? 'Fatal error - Kill switch activated' : 'Connection lost - Kill switch activated'
});
} else {
// Kill switch is OFF - just disconnect and show error
await ProxyManager.disconnect();
await Storage.setProxyEnabled(false);
broadcastMessage({
action: 'statusUpdate',
status: 'error',
error: details.error || 'Proxy connection error'
});
}
});
// ============================================
// Privacy Protection Functions
// ============================================
/**
* Apply WebRTC Leak Protection
* @param {boolean} enabled - Whether to enable protection
*/
async function applyWebRTCProtection(enabled) {
try {
const value = enabled ? 'disable_non_proxied_udp' : 'default';
await chrome.privacy.network.webRTCIPHandlingPolicy.set({ value });
Utils.log('info', `WebRTC protection ${enabled ? 'enabled' : 'disabled'}`, { policy: value });
return true;
} catch (error) {
Utils.log('error', 'Failed to set WebRTC policy', error);
return false;
}
}
/**
* Apply Kill Switch - blocks all traffic when proxy disconnects unexpectedly
* @param {boolean} activate - Whether to activate (block) or deactivate (unblock)
*/
async function applyKillSwitch(activate) {
try {
if (activate) {
// Kill switch active: block all traffic by setting invalid proxy
const config = {
mode: 'fixed_servers',
rules: {
singleProxy: {
scheme: 'socks5',
host: '127.0.0.1', // Localhost with closed port - blocks all traffic
port: 65535
}
}
};
await chrome.proxy.settings.set({ value: config, scope: 'regular' });
await Storage.setValue('killSwitchActive', true);
Utils.log('info', 'Kill switch ACTIVATED - all traffic blocked');
// Update icon to show blocked state
try {
await chrome.action.setBadgeText({ text: '!' });
await chrome.action.setBadgeBackgroundColor({ color: '#e74c3c' });
} catch (e) {}
} else {
// Deactivate kill switch - clear proxy settings
await chrome.proxy.settings.clear({ scope: 'regular' });
await Storage.setValue('killSwitchActive', false);
Utils.log('info', 'Kill switch DEACTIVATED - traffic restored');
// Clear badge
try {
await chrome.action.setBadgeText({ text: '' });
} catch (e) {}
}
return true;
} catch (error) {
Utils.log('error', 'Failed to apply kill switch', error);
return false;
}
}
/**
* Initialize privacy settings on startup
*/
async function initializePrivacySettings() {
const webrtcProtection = await Storage.getValue(CONFIG.STORAGE_KEYS.WEBRTC_PROTECTION, true);
await applyWebRTCProtection(webrtcProtection);
Utils.log('info', 'Privacy settings initialized');
}
// Initialize privacy settings
initializePrivacySettings();
// ============================================
// Proxy Authentication Handler
// ============================================
/**
* Handle proxy authentication requests
* This is called when a proxy requires username/password
*/
chrome.webRequest.onAuthRequired.addListener(
async (details, callback) => {
Utils.log('info', 'Proxy authentication required', { challenger: details.challenger });
// Only handle proxy authentication
if (!details.isProxy) {
callback({});
return;
}
try {
// Get stored credentials
const customProxy = await Storage.getCustomProxy();
if (customProxy.username && customProxy.password) {
Utils.log('info', 'Providing proxy credentials');
callback({
authCredentials: {
username: customProxy.username,
password: customProxy.password
}
});
} else {
Utils.log('warn', 'No proxy credentials stored');
callback({});
}
} catch (error) {
Utils.log('error', 'Error handling proxy auth', error);
callback({});
}
},
{ urls: ['<all_urls>'] },
['asyncBlocking']
);
Utils.log('info', 'Background service worker ready');

87
js/config.js Normal file
View File

@ -0,0 +1,87 @@
/* ANyONe Extension v2 - Configuration */
const CONFIG = {
// Version
VERSION: '2.0.0',
// Connection Modes
MODES: {
PUBLIC: 'public',
CUSTOM: 'custom'
},
// Default Settings
DEFAULTS: {
MODE: 'public',
PROXY_TIMEOUT: 5000,
AUTO_CONNECT: false,
WEBRTC_PROTECTION: false,
BYPASS_LOCAL: true,
PROXY_SOURCE: 'arweave',
UPDATE_INTERVAL: 0 // manual only
},
// Proxy Sources
PROXY_SOURCES: {
arweave: {
name: 'Arweave',
url: 'https://arweave.net/FjxfWIbSnZb7EaJWbeuWCsBBFWjTppfS3_KHxUP__B8',
icon: 'AR'
},
git: {
name: 'GitBros',
url: 'https://git.debros.io/DeBros/anyone-proxy-list/raw/branch/main/anonproxies.json',
icon: 'GIT'
},
github: {
name: 'GitHub',
url: 'https://raw.githubusercontent.com/DeBrosOfficial/anyone-proxy-list/refs/heads/main/anonproxies.json',
icon: 'GH'
}
},
// External URLs
URLS: {
CHECK_IP: 'https://check.en.anyone.tech/',
DOCS: 'https://docs.anyone.io/',
GITHUB: 'https://github.com/anyone-protocol',
WEBSITE: 'https://anyone.io/'
},
// Timeouts (ms)
TIMEOUTS: {
PROXY_CHECK: 5000,
FETCH: 10000
},
// Storage Keys
STORAGE_KEYS: {
MODE: 'connectionMode',
PROXY_ENABLED: 'proxyEnabled',
PROXY_LIST: 'proxyList',
PROXY_SOURCE: 'proxySource',
CURRENT_PROXY: 'currentProxy',
CUSTOM_IP: 'proxyIP',
CUSTOM_PORT: 'proxyPort',
CUSTOM_USERNAME: 'proxyUsername',
CUSTOM_PASSWORD: 'proxyPassword',
EXCEPTIONS: 'noProxyFor',
AUTO_CONNECT: 'autoConnect',
WEBRTC_PROTECTION: 'webrtcProtection',
KILL_SWITCH: 'killSwitch',
BYPASS_LOCAL: 'bypassLocal',
LAST_UPDATE: 'lastProxyUpdate'
}
};
// Freeze config to prevent modifications
Object.freeze(CONFIG);
Object.freeze(CONFIG.MODES);
Object.freeze(CONFIG.DEFAULTS);
Object.freeze(CONFIG.PROXY_SOURCES);
Object.freeze(CONFIG.URLS);
Object.freeze(CONFIG.TIMEOUTS);
Object.freeze(CONFIG.STORAGE_KEYS);
// ES Module export
export { CONFIG };

View File

@ -1,226 +1,558 @@
const proxyIP = document.getElementById("proxyIP");
const proxyPort = document.getElementById("proxyPort");
const noProxyFor = document.getElementById("noProxyFor");
const saveSettings = document.getElementById("saveSettings");
const disableProxy = document.getElementById("disableProxy");
const statusMessage = document.getElementById("statusMessage");
const checkAnyoneButton = document.getElementById('checkAnyoneButton');
/* ANyONe Extension v2 - Options Page Controller */
// Validate IP address
function isValidIP(ip) {
const ipRegex = /^(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})\.(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})\.(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})\.(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})$/;
return ipRegex.test(ip);
}
// ES Module imports
import { Utils } from './utils.js';
// Validate port number
function isValidPort(port) {
const num = parseInt(port, 10);
return num > 0 && num <= 65535;
}
// ============================================
// DOM Elements
// ============================================
// Load saved settings on page load
chrome.storage.local.get(["proxyIP", "proxyPort", "proxyType", "noProxyFor"], (settings) => {
if (chrome.runtime.lastError) {
console.error("Error retrieving settings:", chrome.runtime.lastError);
return;
}
proxyIP.value = settings.proxyIP || "";
proxyPort.value = settings.proxyPort || "";
noProxyFor.value = settings.noProxyFor || "";
});
const elements = {
// General
autoConnect: document.getElementById('auto-connect'),
defaultMode: document.getElementById('default-mode'),
// Function to check internet connectivity with a timeout
function checkInternetConnection(host, port) {
return new Promise((resolve, reject) => {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout
// Privacy
webrtcProtection: document.getElementById('webrtc-protection'),
killSwitch: document.getElementById('kill-switch'),
bypassLocal: document.getElementById('bypass-local'),
const proxyConfig = {
mode: "fixed_servers",
rules: {
singleProxy: {
scheme: "socks5",
host: host,
port: parseInt(port, 10)
},
bypassList: [""]
}
// Public Proxies
proxySource: document.getElementById('proxy-source'),
updateInterval: document.getElementById('update-interval'),
sourceName: document.getElementById('source-name'),
sourceUrl: document.getElementById('source-url'),
sourceUpdated: document.getElementById('source-updated'),
btnRefreshProxies: document.getElementById('btn-refresh-proxies'),
// Bypass List
exceptions: document.getElementById('exceptions'),
btnClearBypass: document.getElementById('btn-clear-bypass'),
btnSaveBypass: document.getElementById('btn-save-bypass'),
// Custom Proxy
customIp: document.getElementById('custom-ip'),
customPort: document.getElementById('custom-port'),
customUsername: document.getElementById('custom-username'),
customPassword: document.getElementById('custom-password'),
btnClearCustom: document.getElementById('btn-clear-custom'),
btnTestCustom: document.getElementById('btn-test-custom'),
btnSaveCustom: document.getElementById('btn-save-custom'),
// Toast
toast: document.getElementById('toast'),
toastIcon: document.getElementById('toast-icon'),
toastMessage: document.getElementById('toast-message'),
// Modal
modalOverlay: document.getElementById('modal-overlay'),
modalTitle: document.getElementById('modal-title'),
modalMessage: document.getElementById('modal-message'),
modalCancel: document.getElementById('modal-cancel'),
modalConfirm: document.getElementById('modal-confirm')
};
chrome.proxy.settings.set({ value: proxyConfig, scope: "regular" }, () => {
if (chrome.runtime.lastError) {
clearTimeout(timeoutId);
reject(chrome.runtime.lastError.message);
// ============================================
// Initialization
// ============================================
document.addEventListener('DOMContentLoaded', init);
async function init() {
console.log('[Options] Initializing...');
// Load settings
await loadSettings();
// Setup event listeners
setupEventListeners();
// Update proxy source info
await updateProxySourceInfo();
console.log('[Options] Initialized');
}
// ============================================
// Load Settings
// ============================================
async function loadSettings() {
const response = await sendMessage({ action: 'getSettings' });
if (!response.success) {
console.log('Failed to load settings');
return;
}
fetch('https://check.en.anyone.tech', {
method: 'GET',
mode: 'no-cors',
signal: controller.signal
})
.then(response => {
clearTimeout(timeoutId);
if (response.ok) {
resolve(true); // Connection successful
} else {
reject("Failed to connect via proxy"); // Connection failed
const settings = response.settings;
// General
elements.autoConnect.checked = settings.autoConnect || false;
elements.defaultMode.value = settings.connectionMode || 'public';
// Privacy
elements.webrtcProtection.checked = settings.webrtcProtection !== false;
elements.killSwitch.checked = settings.killSwitch || false;
elements.bypassLocal.checked = settings.bypassLocal !== false;
// Public Proxies
elements.proxySource.value = settings.proxySource || 'git';
elements.updateInterval.value = settings.updateInterval ?? 0;
updateSourceNameDisplay(settings.proxySource || 'git');
// Custom Proxy
elements.customIp.value = settings.proxyIP || '';
elements.customPort.value = settings.proxyPort || '';
elements.customUsername.value = settings.proxyUsername || '';
elements.customPassword.value = settings.proxyPassword || '';
elements.exceptions.value = Array.isArray(settings.noProxyFor)
? settings.noProxyFor.join(', ')
: settings.noProxyFor || '';
}
})
.catch(error => {
clearTimeout(timeoutId);
if (error.name === 'AbortError') {
reject("Connection timed out");
} else {
reject(error.message || "Network error encountered");
// ============================================
// Event Listeners
// ============================================
function setupEventListeners() {
// Auto-save on toggle changes
elements.autoConnect.addEventListener('change', saveGeneralSettings);
elements.defaultMode.addEventListener('change', saveGeneralSettings);
elements.webrtcProtection.addEventListener('change', savePrivacySettings);
elements.killSwitch.addEventListener('change', savePrivacySettings);
elements.bypassLocal.addEventListener('change', handleBypassLocalChange);
elements.proxySource.addEventListener('change', handleProxySourceChange);
elements.updateInterval.addEventListener('change', saveProxySourceSettings);
// Bypass list buttons
elements.btnClearBypass.addEventListener('click', clearBypassList);
elements.btnSaveBypass.addEventListener('click', saveBypassList);
// Buttons
elements.btnRefreshProxies.addEventListener('click', refreshProxies);
elements.btnClearCustom.addEventListener('click', clearCustomProxy);
elements.btnTestCustom.addEventListener('click', testCustomProxy);
elements.btnSaveCustom.addEventListener('click', saveCustomProxy);
}
// ============================================
// Save Settings
// ============================================
async function saveGeneralSettings() {
await sendMessage({
action: 'saveSettings',
settings: {
autoConnect: elements.autoConnect.checked,
connectionMode: elements.defaultMode.value
}
});
showToast('General settings saved');
}
async function savePrivacySettings() {
await sendMessage({
action: 'saveSettings',
settings: {
webrtcProtection: elements.webrtcProtection.checked,
killSwitch: elements.killSwitch.checked,
bypassLocal: elements.bypassLocal.checked
}
});
showToast('Privacy settings saved');
}
async function handleBypassLocalChange() {
if (!elements.bypassLocal.checked) {
// Revert toggle immediately, wait for confirmation
elements.bypassLocal.checked = true;
// Show confirmation modal
showModal(
'Disable Local Network Access?',
'This will make local devices (printers, NAS, router, etc) unreachable while connected to the proxy.',
async () => {
// User confirmed - disable local network access
elements.bypassLocal.checked = false;
await saveBypassLocalSetting();
showToast('Local network access disabled', 'error');
}
);
} else {
// Enabling - no confirmation needed
await saveBypassLocalSetting();
showToast('Local network access enabled', 'success');
}
}
async function saveBypassLocalSetting() {
await sendMessage({
action: 'saveSettings',
settings: {
webrtcProtection: elements.webrtcProtection.checked,
killSwitch: elements.killSwitch.checked,
bypassLocal: elements.bypassLocal.checked
}
});
}
// Function to apply Proxy Settings after saving
function applyProxySettings(host, port, exceptions = []) {
const proxyConfig = {
mode: "fixed_servers",
rules: {
singleProxy: {
scheme: "socks5",
host: host,
port: parseInt(port, 10)
},
bypassList: exceptions.concat([""])
function showModal(title, message, onConfirm) {
elements.modalTitle.textContent = title;
elements.modalMessage.textContent = message;
elements.modalOverlay.classList.add('show');
// Remove old listeners
const newCancelBtn = elements.modalCancel.cloneNode(true);
const newConfirmBtn = elements.modalConfirm.cloneNode(true);
elements.modalCancel.parentNode.replaceChild(newCancelBtn, elements.modalCancel);
elements.modalConfirm.parentNode.replaceChild(newConfirmBtn, elements.modalConfirm);
elements.modalCancel = newCancelBtn;
elements.modalConfirm = newConfirmBtn;
// Add new listeners
elements.modalCancel.addEventListener('click', () => {
elements.modalOverlay.classList.remove('show');
});
elements.modalConfirm.addEventListener('click', () => {
elements.modalOverlay.classList.remove('show');
if (onConfirm) onConfirm();
});
// Close on overlay click
elements.modalOverlay.addEventListener('click', (e) => {
if (e.target === elements.modalOverlay) {
elements.modalOverlay.classList.remove('show');
}
});
}
async function saveProxySourceSettings() {
await sendMessage({
action: 'saveSettings',
settings: {
updateInterval: parseInt(elements.updateInterval.value, 10)
}
});
showToast('Proxy settings saved');
}
async function saveBypassList() {
const exceptions = elements.exceptions.value
.split(',')
.map(e => e.trim())
.filter(e => e.length > 0);
await sendMessage({
action: 'saveSettings',
settings: {
noProxyFor: exceptions
}
});
showToast('Bypass list saved', 'success');
}
async function clearBypassList() {
elements.exceptions.value = '';
await sendMessage({
action: 'saveSettings',
settings: {
noProxyFor: []
}
});
showToast('Bypass list cleared', 'success');
}
async function handleProxySourceChange() {
const source = elements.proxySource.value;
await sendMessage({
action: 'saveSettings',
settings: {
proxySource: source
}
});
updateSourceNameDisplay(source);
showToast('Proxy source changed');
}
function updateSourceNameDisplay(source) {
const names = {
git: 'GitBros',
github: 'GitHub',
arweave: 'Arweave'
};
const urls = {
git: 'git.debros.io/DeBros/anyone-proxy-list',
github: 'github.com/DeBrosOfficial/anyone-proxy-list',
arweave: 'arweave.net/FjxfWIbS...B8'
};
const fullUrls = {
git: 'https://git.debros.io/DeBros/anyone-proxy-list',
github: 'https://github.com/DeBrosOfficial/anyone-proxy-list',
arweave: 'https://arweave.net/FjxfWIbSnZb7EaJWbeuWCsBBFWjTppfS3_KHxUP__B8'
};
const externalIcon = `<svg class="icon-external" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
<polyline points="15 3 21 3 21 9"/>
<line x1="10" y1="14" x2="21" y2="3"/>
</svg>`;
elements.sourceName.textContent = names[source] || 'GitBros';
elements.sourceUrl.innerHTML = (urls[source] || urls.git) + ' ' + externalIcon;
elements.sourceUrl.href = fullUrls[source] || fullUrls.git;
}
chrome.proxy.settings.set({ value: proxyConfig, scope: "regular" }, () => {
if (chrome.runtime.lastError) {
statusMessage.textContent = "Error applying proxy: " + chrome.runtime.lastError.message;
statusMessage.style.color = "red";
async function saveCustomProxy() {
const ip = elements.customIp.value.trim();
const port = elements.customPort.value.trim();
const username = elements.customUsername.value.trim();
const password = elements.customPassword.value;
// Validate
if (!ip) {
showToast('Please enter host address', 'error');
return;
}
if (!Utils.isValidHost(ip)) {
showToast('Invalid IP address or hostname', 'error');
return;
}
if (!port || !Utils.isValidPort(port)) {
showToast('Invalid port number (1-65535)', 'error');
return;
}
// Disable button during save
elements.btnSaveCustom.disabled = true;
const originalText = elements.btnSaveCustom.innerHTML;
elements.btnSaveCustom.innerHTML = '<span>Saving...</span>';
try {
// Save settings (bypass list is saved separately in Privacy section)
await sendMessage({
action: 'saveSettings',
settings: {
proxyIP: ip,
proxyPort: parseInt(port, 10),
proxyUsername: username,
proxyPassword: password
}
});
showToast('Custom proxy settings saved', 'success');
} finally {
elements.btnSaveCustom.disabled = false;
elements.btnSaveCustom.innerHTML = originalText;
}
}
async function clearCustomProxy() {
// Disable button during clear
elements.btnClearCustom.disabled = true;
const originalText = elements.btnClearCustom.innerHTML;
elements.btnClearCustom.innerHTML = '<span>Clearing...</span>';
try {
// Clear from storage first
const response = await sendMessage({
action: 'clearCustomProxy'
});
if (response.success) {
// Clear custom proxy UI fields
elements.customIp.value = '';
elements.customPort.value = '';
elements.customUsername.value = '';
elements.customPassword.value = '';
showToast('Custom proxy settings cleared', 'success');
} else {
console.log(`Proxy applied: ${host}:${port}`);
statusMessage.textContent = `Proxy applied: ${host}:${port}`;
statusMessage.style.color = "#2ecc71";
clearStatusMessage();
showToast('Failed to clear settings', 'error');
}
} catch (error) {
showToast('Failed to clear settings', 'error');
} finally {
elements.btnClearCustom.disabled = false;
elements.btnClearCustom.innerHTML = originalText;
}
}
// ============================================
// Proxy Actions
// ============================================
async function refreshProxies() {
elements.btnRefreshProxies.disabled = true;
const originalText = elements.btnRefreshProxies.innerHTML;
elements.btnRefreshProxies.innerHTML = '<span>Refreshing...</span>';
const source = elements.proxySource.value || 'arweave';
const response = await sendMessage({ action: 'fetchProxies', source });
elements.btnRefreshProxies.disabled = false;
elements.btnRefreshProxies.innerHTML = originalText;
if (response.success) {
const sourceNames = { arweave: 'Arweave', git: 'GitBros', github: 'GitHub' };
const usedName = sourceNames[response.usedSource] || response.usedSource;
if (response.usedSource && response.usedSource !== source) {
showToast(`${response.proxies.length} proxies from ${usedName} (fallback)`, 'success');
// Update dropdown to show actual source used
elements.proxySource.value = response.usedSource;
updateSourceNameDisplay(response.usedSource);
} else {
showToast(`Updated: ${response.proxies.length} proxies`, 'success');
}
await updateProxySourceInfo();
} else {
showToast('All proxy sources failed', 'error');
}
}
async function updateProxySourceInfo() {
const response = await sendMessage({ action: 'getProxyList' });
if (response.success) {
const lastUpdate = response.lastUpdate;
if (lastUpdate) {
const relative = Utils.formatRelativeTime(lastUpdate);
elements.sourceUpdated.textContent = `Last updated: ${relative}`;
} else {
elements.sourceUpdated.textContent = 'Last updated: Never';
}
}
}
async function testCustomProxy() {
const ip = elements.customIp.value.trim();
const port = elements.customPort.value.trim();
const username = elements.customUsername.value.trim();
const password = elements.customPassword.value;
if (!ip || !port) {
showToast('Please enter IP and port', 'error');
return;
}
elements.btnTestCustom.disabled = true;
const originalText = elements.btnTestCustom.innerHTML;
elements.btnTestCustom.innerHTML = '<span>Testing...</span>';
// Get current connection state BEFORE testing
const statusBefore = await sendMessage({ action: 'getStatus' });
const wasConnected = statusBefore.enabled;
const previousMode = statusBefore.mode;
// Save credentials temporarily so auth handler can use them
if (username || password) {
await sendMessage({
action: 'saveSettings',
settings: {
proxyUsername: username,
proxyPassword: password
}
});
}
// Function to clear status message after a delay
function clearStatusMessage() {
// Show testing state
showToast(`Testing ${ip}:${port}...`, 'info');
const response = await sendMessage({
action: 'testProxy',
proxy: { host: ip, port: parseInt(port, 10) }
});
elements.btnTestCustom.disabled = false;
elements.btnTestCustom.innerHTML = originalText;
if (response.working) {
// Save the proxy settings
await sendMessage({
action: 'saveSettings',
settings: {
proxyIP: ip,
proxyPort: parseInt(port, 10),
proxyUsername: username,
proxyPassword: password
}
});
// Connect to the proxy
const connectResponse = await sendMessage({
action: 'connect',
mode: 'custom'
});
if (connectResponse.success) {
showToast(`Connected to ${ip}:${port}`, 'success');
} else {
showToast(`Proxy working but failed to connect`, 'error');
}
} else {
// Test failed - handle based on previous connection state
if (wasConnected && previousMode === 'public') {
// User was connected to public proxy - restore that connection
showToast(`Test failed. Restoring public proxy...`, 'info');
const reconnectResponse = await sendMessage({
action: 'connect',
mode: 'public'
});
if (reconnectResponse.success) {
showToast(`Proxy ${ip}:${port} not responding. Reconnected to public proxy.`, 'error');
} else {
showToast(`Proxy ${ip}:${port} not responding. Failed to restore connection.`, 'error');
}
} else if (wasConnected && previousMode === 'custom') {
// User was connected to custom proxy - they're testing a different one, disconnect
await sendMessage({ action: 'disconnect' });
showToast(`Proxy ${ip}:${port} is not responding`, 'error');
} else {
// User was not connected - just show error
showToast(`Proxy ${ip}:${port} is not responding`, 'error');
}
}
}
// ============================================
// Utilities
// ============================================
function sendMessage(message) {
return new Promise((resolve) => {
chrome.runtime.sendMessage(message, (response) => {
resolve(response || { success: false, error: 'No response' });
});
});
}
function showToast(message, type = 'success') {
elements.toastMessage.textContent = message;
elements.toast.className = `toast show ${type}`;
setTimeout(() => {
statusMessage.textContent = "";
}, 5000); // Clear message after 5 seconds (5000 milliseconds)
elements.toast.classList.remove('show');
}, 3000);
}
let isCheckingProxy = false;
// Save settings when clicking "Save Settings"
saveSettings.addEventListener("click", () => {
if (isCheckingProxy) return; // If a check is already in progress, do nothing
if (!isValidIP(proxyIP.value)) {
statusMessage.textContent = "Invalid IP address.";
statusMessage.style.color = "red";
return;
// Listen for messages from background
chrome.runtime.onMessage.addListener((message) => {
if (message.action === 'statusUpdate') {
if (message.status === 'connected') {
showToast('Proxy connected', 'success');
} else if (message.status === 'disconnected') {
showToast('Proxy disconnected');
} else if (message.status === 'error') {
showToast(message.error || 'Connection error', 'error');
}
if (!isValidPort(proxyPort.value)) {
statusMessage.textContent = "Invalid port number. Must be between 1 and 65535.";
statusMessage.style.color = "red";
return;
}
statusMessage.textContent = "Please wait...";
statusMessage.style.color = "#f39c12";
// Disable the saveSettings button
saveSettings.disabled = true;
isCheckingProxy = true;
const noProxyExceptions = noProxyFor.value.split(',').map(ex => ex.trim());
const filteredExceptions = noProxyExceptions.filter(ex => ex !== '');
checkInternetConnection(proxyIP.value, proxyPort.value)
.then(() => {
chrome.storage.local.set({
proxyIP: proxyIP.value,
proxyPort: proxyPort.value,
proxyType: "custom",
noProxyFor: filteredExceptions.join(", "),
proxyEnabled: true
}, () => {
if (chrome.runtime.lastError) {
statusMessage.textContent = "Error saving settings: " + chrome.runtime.lastError.message;
statusMessage.style.color = "red";
} else {
statusMessage.textContent = "Proxy settings saved and connection verified!";
statusMessage.style.color = "#2ecc71";
applyProxySettings(proxyIP.value, proxyPort.value, filteredExceptions);
// Send response to the message sender
chrome.runtime.sendMessage({ action: "updateProxy", type: "custom", proxy: { host: proxyIP.value, port: parseInt(proxyPort.value) }, exceptions: filteredExceptions }, (response) => {
if (chrome.runtime.lastError) {
console.error("Error sending message:", chrome.runtime.lastError);
} else {
console.log("Response received:", response);
}
clearStatusMessage();
});
}
});
})
.catch((error) => {
statusMessage.textContent = `Proxy connection failed: ${error}. Settings not applied.`;
statusMessage.style.color = "red";
chrome.proxy.settings.clear({});
// Send a message indicating proxy setup failed
chrome.runtime.sendMessage({ action: "proxyFailed", error: error }, (response) => {
if (chrome.runtime.lastError) {
console.error("Error sending message:", chrome.runtime.lastError);
} else {
console.log("Response received for proxy failure:", response);
}
clearStatusMessage();
});
})
.finally(() => {
// Re-enable the saveSettings button after the check is complete
saveSettings.disabled = false;
isCheckingProxy = false;
});
});
disableProxy.addEventListener("click", () => {
chrome.proxy.settings.clear({}, () => {
if (chrome.runtime.lastError) {
statusMessage.textContent = "Error disabling proxy: " + chrome.runtime.lastError.message;
statusMessage.style.color = "red";
} else {
statusMessage.textContent = "Proxy has been disabled!";
statusMessage.style.color = "#e74c3c";
clearStatusMessage();
console.log("Proxy settings disabled.");
chrome.storage.local.get(["noProxyFor"], (result) => {
chrome.storage.local.set({
proxyType: null,
noProxyFor: result.noProxyFor,
proxyEnabled: false
}, () => {
if (chrome.runtime.lastError) {
console.error("Error updating storage:", chrome.runtime.lastError);
} else {
chrome.runtime.sendMessage({ action: "disableProxy" });
}
});
});
clearStatusMessage();
}
});
});
// Open Check Anyone page
checkAnyoneButton.addEventListener("click", () => {
window.open("https://check.en.anyone.tech/", "_blank");
});

View File

@ -1,188 +1,695 @@
document.addEventListener("DOMContentLoaded", () => {
const proxyToggle = document.getElementById("proxyToggle");
const statusMessage = document.getElementById("statusMessage");
const optionsButton = document.getElementById("optionsButton");
const checkAnyoneButton = document.getElementById("checkAnyoneButton");
const dappStoreButton = document.getElementById("dappStoreButton");
const updateProxiesButton = document.getElementById("updateProxiesButton");
/* ANyONe Extension v2 - Popup Controller */
if (!checkAnyoneButton) {
checkAnyoneButton = document.createElement("button");
checkAnyoneButton.id = "checkAnyoneButton";
checkAnyoneButton.textContent = "Check ANyONe";
checkAnyoneButton.style.marginBottom = "10px";
checkAnyoneButton.addEventListener("click", () => {
window.open("https://check.en.anyone.tech/", "_blank");
// ES Module imports
import { CONFIG } from './config.js';
import { Utils } from './utils.js';
// ============================================
// State
// ============================================
const state = {
mode: 'public',
connected: false,
connecting: false,
blocked: false, // Kill switch active
currentProxy: null,
proxyCount: 0
};
// ============================================
// DOM Elements
// ============================================
const elements = {
// Mode tabs
modeTabs: document.querySelectorAll('.mode-tab'),
modeContents: document.querySelectorAll('.mode-content'),
// Public mode
proxyMode: document.getElementById('proxy-mode'),
proxyCount: document.getElementById('proxy-count'),
btnNextProxy: document.getElementById('btn-next-proxy'),
// Custom mode
customIp: document.getElementById('custom-ip'),
customPort: document.getElementById('custom-port'),
btnTestCustom: document.getElementById('btn-test-custom'),
// Connect button
btnConnect: document.getElementById('btn-connect'),
// Status
statusCard: document.getElementById('status-card'),
statusDot: document.getElementById('status-dot'),
statusText: document.getElementById('status-text'),
statusIp: document.getElementById('status-ip'),
// Quick actions
btnCheckIp: document.getElementById('btn-check-ip'),
btnRefresh: document.getElementById('btn-refresh'),
btnSettings: document.getElementById('btn-settings')
};
// ============================================
// Initialization
// ============================================
document.addEventListener('DOMContentLoaded', init);
async function init() {
console.log('[Popup] Initializing...');
// Prevent scroll
document.body.style.overflow = 'hidden';
document.documentElement.style.overflow = 'hidden';
window.addEventListener('scroll', (e) => {
window.scrollTo(0, 0);
});
document.addEventListener('wheel', (e) => {
e.preventDefault();
}, { passive: false });
document.addEventListener('touchmove', (e) => {
e.preventDefault();
}, { passive: false });
// Setup event listeners
setupEventListeners();
// Load initial state
await loadState();
// Load custom proxy settings
await loadCustomProxy();
// Load proxy info for public mode
await loadProxyInfo();
console.log('[Popup] Initialized', state);
}
if (!dappStoreButton) {
dappStoreButton = document.createElement("button");
dappStoreButton.id = "dappStoreButton";
dappStoreButton.textContent = "dApp Store";
dappStoreButton.style.marginBottom = "10px";
dappStoreButton.addEventListener("click", () => {
const storeUrl = chrome.runtime.getURL("store.html");
chrome.tabs.create({ url: storeUrl });
// ============================================
// Event Listeners
// ============================================
function setupEventListeners() {
// Mode tabs
elements.modeTabs.forEach(tab => {
tab.addEventListener('click', () => switchMode(tab.dataset.mode));
});
// Connect button
elements.btnConnect.addEventListener('click', handleConnect);
// Public mode
elements.btnNextProxy.addEventListener('click', handleNextProxy);
// Custom mode
elements.btnTestCustom.addEventListener('click', testCustomProxy);
// Quick actions
elements.btnCheckIp.addEventListener('click', () => {
sendMessage({ action: 'openUrl', url: CONFIG.URLS.CHECK_IP });
});
elements.btnRefresh.addEventListener('click', refreshProxies);
elements.btnSettings.addEventListener('click', () => {
sendMessage({ action: 'openOptions' });
});
// Listen for status updates from background
chrome.runtime.onMessage.addListener(handleBackgroundMessage);
}
if (updateProxiesButton) {
updateProxiesButton.addEventListener("click", () => {
console.log("Update Proxies button clicked in popup.js");
statusMessage.textContent = "Updating proxies...";
statusMessage.style.color = "#f39c12"; // Orange for loading
chrome.runtime.sendMessage({ action: "updateProxies" }, (response) => {
if (response && response.success) {
console.log('Proxy list update was successful');
statusMessage.textContent = 'Proxies updated successfully!';
statusMessage.style.color = "#2ecc71"; // Green for success
// ============================================
// State Management
// ============================================
async function loadState() {
const response = await sendMessage({ action: 'getStatus' });
if (response.success) {
state.mode = response.mode || 'public';
state.connected = response.enabled;
state.blocked = response.killSwitchActive || false;
state.currentProxy = response.currentProxy;
// Update UI
updateModeUI();
updateConnectionUI();
}
}
async function loadProxyInfo() {
console.log('[Popup] Loading proxy info...');
const response = await sendMessage({ action: 'getProxyList' });
console.log('[Popup] Proxy list response:', response);
if (response.success) {
state.proxyCount = response.count || 0;
updateProxyInfo();
// Show warning if no proxies available
if (state.proxyCount === 0 && !state.connected && !state.connecting) {
showNoProxiesWarning();
}
} else {
console.log('Proxy list update failed');
statusMessage.textContent = 'Failed to update proxies.';
statusMessage.style.color = "#e74c3c"; // Red for failure
console.log('[Popup] Failed to load proxy info:', response.error);
elements.proxyCount.textContent = 'Error loading';
showNoProxiesWarning();
}
// Clear the update message after 3 seconds and reinitialize the UI
}
function updateProxyInfo() {
elements.proxyMode.textContent = 'Auto (Fastest)';
elements.proxyCount.textContent = `${state.proxyCount} nodes`;
}
function showNoProxiesWarning() {
elements.statusText.textContent = 'No proxies';
elements.statusText.style.color = 'var(--color-warning)';
elements.statusIp.textContent = 'Click Refresh to load proxy list';
elements.statusIp.style.display = 'block';
elements.statusIp.style.color = 'var(--color-text-muted)';
}
async function loadCustomProxy() {
const response = await sendMessage({ action: 'getSettings' });
if (response.success && response.settings) {
const settings = response.settings;
if (settings.proxyIP) {
elements.customIp.value = settings.proxyIP;
}
if (settings.proxyPort) {
elements.customPort.value = settings.proxyPort;
}
}
}
// ============================================
// Mode Switching
// ============================================
function switchMode(mode) {
state.mode = mode;
// Update tabs
elements.modeTabs.forEach(tab => {
tab.classList.toggle('active', tab.dataset.mode === mode);
});
// Update content
elements.modeContents.forEach(content => {
content.classList.toggle('active', content.id === `mode-${mode}`);
});
// Save mode
sendMessage({ action: 'setMode', mode });
}
function updateModeUI() {
// Activate correct tab and content
elements.modeTabs.forEach(tab => {
tab.classList.toggle('active', tab.dataset.mode === state.mode);
});
elements.modeContents.forEach(content => {
content.classList.toggle('active', content.id === `mode-${state.mode}`);
});
}
// ============================================
// Next Proxy (Load Balancing)
// ============================================
async function handleNextProxy() {
if (!state.connected) {
showError('Connect first to switch proxy');
return;
}
// Show switching state (orange)
state.connecting = true;
updateConnectionUI();
elements.statusText.textContent = 'Switching...';
elements.statusText.style.color = 'var(--color-warning)';
elements.statusIp.style.display = 'none';
elements.btnNextProxy.disabled = true;
elements.btnNextProxy.textContent = 'Switching...';
const response = await sendMessage({ action: 'nextProxy' });
state.connecting = false;
elements.statusText.style.color = '';
elements.btnNextProxy.disabled = false;
elements.btnNextProxy.innerHTML = `
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="9 18 15 12 9 6"/>
</svg>
Next Proxy
`;
if (response.success) {
state.currentProxy = response.proxy;
showSuccess(`Switched to ${response.proxy?.host || 'next proxy'}`);
updateConnectionUI();
} else {
showError(response.error || 'Failed to switch proxy');
}
}
// ============================================
// Connection
// ============================================
async function handleConnect() {
if (state.connecting) return;
if (state.connected) {
await disconnect();
} else {
await connect();
}
}
async function connect() {
state.connecting = true;
updateConnectionUI();
// If already connected, disconnect first before switching modes
if (state.connected) {
await sendMessage({ action: 'disconnect' });
state.connected = false;
state.currentProxy = null;
}
const options = {};
let customIp = null;
let customPort = null;
if (state.mode === 'public') {
options.country = 'auto';
}
// Save custom proxy before connecting if in custom mode
if (state.mode === 'custom') {
customIp = elements.customIp.value.trim();
customPort = elements.customPort.value.trim();
if (!customIp || !customPort) {
state.connecting = false;
showError('Please enter host and port');
return;
}
// Validate host and port
if (!Utils.isValidHost(customIp)) {
state.connecting = false;
showError('Invalid IP address or hostname');
return;
}
if (!Utils.isValidPort(customPort)) {
state.connecting = false;
showError('Invalid port (1-65535)');
return;
}
// Show testing state with IP:port
elements.statusText.textContent = 'Connecting...';
elements.statusText.style.color = 'var(--color-warning)';
elements.statusIp.textContent = `${customIp}:${customPort}`;
elements.statusIp.style.display = 'block';
// Save custom proxy settings
await sendMessage({
action: 'saveSettings',
settings: {
proxyIP: customIp,
proxyPort: parseInt(customPort, 10)
}
});
}
const response = await sendMessage({
action: 'connect',
mode: state.mode,
options
});
state.connecting = false;
elements.statusText.style.color = '';
if (response.success) {
state.connected = true;
state.currentProxy = response.proxy;
} else {
if (state.mode === 'custom') {
state.connected = false;
state.currentProxy = null;
showErrorPersistent(`Proxy ${customIp}:${customPort} is not responding`);
return;
}
showError(response.error || 'Connection failed');
}
updateConnectionUI();
}
async function disconnect() {
state.connecting = true;
updateConnectionUI();
const response = await sendMessage({ action: 'disconnect' });
state.connecting = false;
state.connected = !response.success;
state.currentProxy = null;
updateConnectionUI();
}
function updateConnectionUI() {
const btn = elements.btnConnect;
const card = elements.statusCard;
const dot = elements.statusDot;
const statusText = elements.statusText;
const statusIp = elements.statusIp;
// Remove all state classes
btn.classList.remove('connected', 'connecting', 'error', 'blocked');
card.classList.remove('connected', 'connecting', 'disconnected', 'error', 'blocked');
dot.classList.remove('online', 'offline', 'connecting', 'error', 'blocked');
// Reset text styles
statusText.style.color = '';
statusIp.style.color = '';
if (state.connecting) {
btn.classList.add('connecting');
card.classList.add('connecting');
dot.classList.add('connecting');
statusText.textContent = 'Connecting...';
statusIp.style.display = 'none';
btn.disabled = true;
} else if (state.blocked) {
// Kill switch is active - traffic blocked
btn.classList.add('blocked');
card.classList.add('blocked');
dot.classList.add('blocked');
statusText.textContent = 'BLOCKED';
statusText.style.color = 'var(--color-error)';
statusIp.textContent = 'Kill Switch active - Connect to unblock';
statusIp.style.display = 'block';
statusIp.style.color = 'var(--color-warning)';
btn.disabled = false;
} else if (state.connected) {
btn.classList.add('connected');
card.classList.add('connected');
dot.classList.add('online');
statusText.textContent = 'Connected';
btn.disabled = false;
// Show IP
if (state.currentProxy) {
statusIp.textContent = state.currentProxy.host || '-';
statusIp.style.display = 'block';
}
} else {
card.classList.add('disconnected');
dot.classList.add('offline');
statusText.textContent = 'Disconnected';
statusIp.style.display = 'none';
btn.disabled = false;
}
}
// ============================================
// Custom Proxy
// ============================================
async function testCustomProxy() {
const ip = elements.customIp.value.trim();
const port = elements.customPort.value.trim();
if (!ip || !port) {
showError('Please enter host and port');
return;
}
// Validate host and port
if (!Utils.isValidHost(ip)) {
showError('Invalid IP address or hostname');
return;
}
if (!Utils.isValidPort(port)) {
showError('Invalid port (1-65535)');
return;
}
// Save previous state
const previousConnected = state.connected;
const previousProxy = state.currentProxy;
// Set connecting state (orange)
state.connecting = true;
updateConnectionUI();
elements.statusText.textContent = 'Testing...';
elements.statusText.style.color = 'var(--color-warning)';
elements.statusIp.textContent = `${ip}:${port}`;
elements.statusIp.style.display = 'block';
elements.btnTestCustom.disabled = true;
elements.btnTestCustom.textContent = 'Testing...';
const response = await sendMessage({
action: 'testProxy',
proxy: { host: ip, port: parseInt(port, 10) }
});
// Reset connecting state
state.connecting = false;
elements.statusText.style.color = '';
elements.btnTestCustom.disabled = false;
elements.btnTestCustom.textContent = 'Test Connection';
if (response.working) {
// Proxy is working, now actually connect to it
showSuccess(`Proxy ${ip}:${port} is working!`);
// Save custom proxy settings and connect
await sendMessage({
action: 'saveSettings',
settings: {
proxyIP: ip,
proxyPort: parseInt(port, 10)
}
});
// Actually connect through the proxy
const connectResponse = await sendMessage({
action: 'connect',
mode: 'custom'
});
if (connectResponse.success) {
state.connected = true;
state.currentProxy = connectResponse.proxy || { host: ip, port: parseInt(port, 10) };
}
updateConnectionUI();
} else {
// Stay in error state, don't restore
state.connected = false;
state.currentProxy = null;
showErrorPersistent(`Proxy ${ip}:${port} is not responding`);
}
}
// ============================================
// Utilities
// ============================================
async function refreshProxies() {
const refreshIcon = elements.btnRefresh.querySelector('.icon');
if (refreshIcon) {
refreshIcon.classList.add('spinning');
}
// Get the stored proxy source setting
const settings = await sendMessage({ action: 'getSettings' });
const source = settings.settings?.proxySource || 'arweave';
const response = await sendMessage({ action: 'fetchProxies', source });
if (refreshIcon) {
refreshIcon.classList.remove('spinning');
}
if (response.success) {
// Show which source was used (especially if fallback occurred)
const sourceNames = { arweave: 'Arweave', git: 'GitBros', github: 'GitHub' };
const usedName = sourceNames[response.usedSource] || response.usedSource;
if (response.usedSource && response.usedSource !== source) {
showSuccess(`${response.proxies.length} proxies (fallback: ${usedName})`);
} else {
showSuccess(`Updated: ${response.proxies.length} proxies`);
}
await loadProxyInfo();
} else {
showError('All sources failed');
}
}
function sendMessage(message) {
return new Promise((resolve) => {
chrome.runtime.sendMessage(message, (response) => {
resolve(response || { success: false, error: 'No response' });
});
});
}
function handleBackgroundMessage(message) {
console.log('[Popup] Background message:', message);
switch (message.action) {
case 'statusUpdate':
if (message.status === 'connected') {
state.connected = true;
state.connecting = false;
state.blocked = false;
state.currentProxy = message.proxy;
updateConnectionUI();
} else if (message.status === 'disconnected') {
state.connected = false;
state.connecting = false;
state.blocked = false;
state.currentProxy = null;
updateConnectionUI();
} else if (message.status === 'connecting') {
state.connecting = true;
state.blocked = false;
updateConnectionUI();
} else if (message.status === 'blocked') {
state.connected = false;
state.connecting = false;
state.blocked = true;
state.currentProxy = null;
updateConnectionUI();
} else if (message.status === 'error') {
state.connected = false;
state.connecting = false;
state.currentProxy = null;
// showError handles its own UI update, don't call updateConnectionUI
showError(message.error || 'Connection error');
}
break;
case 'proxyError':
showError(`Proxy error: ${message.error}`);
break;
case 'proxiesUpdated':
loadProxyInfo();
break;
}
}
function showError(message) {
console.log('[Popup] Error:', message);
// Reset state to disconnected
state.connected = false;
state.connecting = false;
state.currentProxy = null;
// Update status card
elements.statusCard.classList.remove('connected', 'connecting', 'disconnected');
elements.statusCard.classList.add('error');
// "Error" in red
elements.statusText.textContent = 'Error';
elements.statusText.style.color = 'var(--color-error)';
// Message below in default color
elements.statusIp.textContent = message;
elements.statusIp.style.display = 'block';
elements.statusIp.style.color = '';
// Update dot to error state
elements.statusDot.classList.remove('online', 'offline', 'connecting');
elements.statusDot.classList.add('error');
// Update button to error state
elements.btnConnect.classList.remove('connected', 'connecting');
elements.btnConnect.classList.add('error');
elements.btnConnect.disabled = false;
// After 3 seconds, just reset the error styling but keep showing "Disconnected" state
setTimeout(() => {
initializeUI();
}, 3000);
});
});
}
const buttonContainer = document.querySelector(".button-container");
if (buttonContainer) {
buttonContainer.innerHTML = '';
buttonContainer.appendChild(optionsButton);
buttonContainer.appendChild(dappStoreButton);
buttonContainer.appendChild(updateProxiesButton);
}
if (statusMessage && !checkAnyoneButton.parentNode) {
statusMessage.parentNode.insertBefore(checkAnyoneButton, statusMessage.nextSibling);
}
function updateStatusMessage(isEnabled, proxyType, proxy) {
if (isEnabled) {
if (proxyType === "custom") {
statusMessage.textContent = `Custom Proxy is ENABLED and routing through ${proxy.host}:${proxy.port}`;
statusMessage.style.color = "#2ecc71"; // Green for custom
} else {
statusMessage.textContent = `Public Proxy is ENABLED and routing through ${proxy.host}:${proxy.port}`;
statusMessage.style.color = "#03bdc5"; // Blue for public
}
} else {
statusMessage.textContent = "Proxy is DISABLED";
statusMessage.style.color = "#e74c3c";
}
}
function updateUI(isEnabled, proxyType, proxy) {
proxyToggle.checked = isEnabled;
updateStatusMessage(isEnabled, proxyType, proxy);
}
function updateStatusFromBackground(data) {
statusMessage.textContent = data.message;
statusMessage.style.color = data.color;
setTimeout(() => {
statusMessage.textContent = "";
initializeUI(); // Reinitialize the UI to show the current status
elements.statusCard.classList.remove('error');
elements.statusCard.classList.add('disconnected');
elements.statusText.textContent = 'Disconnected';
elements.statusText.style.color = '';
elements.statusDot.classList.remove('error');
elements.statusDot.classList.add('offline');
elements.btnConnect.classList.remove('error');
}, 3000);
}
// Initialize the toggle state
function initializeUI() {
chrome.storage.local.get(["proxyEnabled", "proxyType", "currentProxy", "proxyIP", "proxyPort"], (data) => {
const isEnabled = data.proxyEnabled || false;
const proxyType = data.proxyType || "public";
const currentProxy = proxyType === "custom"
? { host: data.proxyIP || "127.0.0.1", port: data.proxyPort || 9050 }
: (data.currentProxy || { host: "82.208.21.140", port: 9052 }); // Default public Proxy
function showErrorPersistent(message) {
console.log('[Popup] Error:', message);
updateUI(isEnabled, proxyType, currentProxy);
});
}
// Update status card
elements.statusCard.classList.remove('connected', 'connecting', 'disconnected');
elements.statusCard.classList.add('error');
initializeUI();
// "Error" in red
elements.statusText.textContent = 'Error';
elements.statusText.style.color = 'var(--color-error)';
proxyToggle.addEventListener("change", () => {
const isEnabled = proxyToggle.checked;
// Message below in default color
elements.statusIp.textContent = message;
elements.statusIp.style.display = 'block';
elements.statusIp.style.color = '';
if (isEnabled) {
chrome.storage.local.get(["proxyType"], (data) => {
if (data.proxyType === "custom") {
chrome.storage.local.get(["proxyIP", "proxyPort"], (settings) => {
chrome.runtime.sendMessage({ action: "updateProxy", type: "custom", proxy: { host: settings.proxyIP, port: settings.proxyPort } }, (response) => {
if (response && response.status === "enabled") {
updateUI(true, "custom", { host: settings.proxyIP, port: settings.proxyPort });
} else {
alert("Failed to enable custom proxy. Please try again.");
proxyToggle.checked = false;
initializeUI();
}
});
});
} else {
chrome.runtime.sendMessage({ action: "enableProxy" }, (response) => {
if (response && response.status === "enabled" && response.proxy) {
chrome.storage.local.set({ proxyEnabled: true, currentProxy: response.proxy, proxyType: "public" });
updateUI(true, "public", response.proxy);
} else {
alert(response.message || "Failed to enable public proxy. Please try again.");
proxyToggle.checked = false;
initializeUI();
}
});
}
});
} else {
chrome.runtime.sendMessage({ action: "disableProxy" }, (response) => {
if (response && response.status === "disabled") {
chrome.storage.local.set({ proxyEnabled: false, proxyType: null });
updateUI(false);
} else {
alert("Failed to disable proxy. Please try again.");
proxyToggle.checked = true;
initializeUI();
}
});
}
});
// Update button
elements.btnConnect.classList.remove('connected', 'connecting');
elements.btnConnect.classList.add('error');
elements.btnConnect.disabled = false;
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === "updatePopupState" || message.action === "disableProxy") {
initializeUI();
} else if (message.action === "showLoadingMessage") {
statusMessage.textContent = message.message;
statusMessage.style.color = "#f39c12"; // Orange for loading
} else if (message.action === "updateStatus") {
updateStatusFromBackground(message);
} else if (message.action === "toggleOff") {
// This is for when a proxy setup fails
proxyToggle.checked = false;
statusMessage.textContent = "Failed to set up proxy. Please check your settings.";
statusMessage.style.color = "#e74c3c"; // Red for error
// You might want to clear this message after some time or on interaction
// Return to disconnected state after 5 seconds
setTimeout(() => {
statusMessage.textContent = "";
}, 5000); // Clear after 5 seconds
state.connected = false;
state.currentProxy = null;
elements.statusCard.classList.remove('error');
elements.statusText.style.color = '';
elements.statusDot.classList.remove('error');
elements.btnConnect.classList.remove('error');
updateConnectionUI();
}, 5000);
}
});
// Open options page
optionsButton.addEventListener("click", () => {
chrome.runtime.openOptionsPage();
});
function showSuccess(message) {
console.log('[Popup] Success:', message);
// Open Check Anyone page
checkAnyoneButton.addEventListener("click", () => {
window.open("https://check.en.anyone.tech/", "_blank");
});
// "Success" in green
elements.statusText.textContent = 'Success';
elements.statusText.style.color = 'var(--color-success)';
// Open dApp Store page
dappStoreButton.addEventListener("click", () => {
const storeUrl = chrome.runtime.getURL("html/store.html");
chrome.tabs.create({ url: storeUrl });
});
});
// Message below in default color
elements.statusIp.textContent = message;
elements.statusIp.style.display = 'block';
elements.statusIp.style.color = '';
setTimeout(() => {
elements.statusText.style.color = '';
updateConnectionUI();
}, 3000);
}

432
js/proxy-manager.js Normal file
View File

@ -0,0 +1,432 @@
/* ANyONe Extension v2 - Proxy Manager */
import { CONFIG } from './config.js';
import { Utils } from './utils.js';
import { Storage } from './storage.js';
const ProxyManager = {
// Current state
_currentProxy: null,
_isEnabled: false,
_proxyList: [],
_currentIndex: 0,
/**
* Initialize proxy manager
*/
async init() {
Utils.log('info', 'Initializing ProxyManager');
// Load state from storage
const [enabled, proxyList, currentProxy] = await Promise.all([
Storage.isProxyEnabled(),
Storage.getProxyList(),
Storage.getCurrentProxy()
]);
this._isEnabled = enabled;
this._proxyList = proxyList;
this._currentProxy = currentProxy;
Utils.log('info', 'ProxyManager initialized', {
enabled,
proxyCount: proxyList.length
});
},
/**
* Create SOCKS5 proxy configuration
* @param {string} host - Proxy host
* @param {number} port - Proxy port
* @param {string[]} exceptions - Bypass list
* @param {boolean} bypassLocal - Allow local network access
* @returns {object}
*/
createProxyConfig(host, port, exceptions = [], bypassLocal = true) {
// Always include localhost and 127.0.0.1
let bypassList = exceptions.concat(['localhost', '127.0.0.1']);
// Add local network ranges if local network access is enabled
if (bypassLocal) {
bypassList.push(
'<local>', // Simple hostnames (no dots)
'10.*', // Class A private network
'172.16.*', // Class B private (172.16.0.0 - 172.31.255.255)
'172.17.*',
'172.18.*',
'172.19.*',
'172.20.*',
'172.21.*',
'172.22.*',
'172.23.*',
'172.24.*',
'172.25.*',
'172.26.*',
'172.27.*',
'172.28.*',
'172.29.*',
'172.30.*',
'172.31.*',
'192.168.*', // Class C private network
'169.254.*', // Link-local
'*.local' // mDNS/Bonjour domains (.local TLD)
);
}
Utils.log('info', 'Creating proxy config', { host, port, bypassList, bypassLocal });
return {
mode: 'fixed_servers',
rules: {
singleProxy: {
scheme: 'socks5',
host: host,
port: parseInt(port, 10)
},
bypassList: bypassList
}
};
},
/**
* Apply proxy settings
* @param {string} host - Proxy host
* @param {number} port - Proxy port
* @param {string[]} exceptions - Bypass list
* @param {boolean} bypassLocal - Allow local network access
* @returns {Promise<boolean>}
*/
async applyProxy(host, port, exceptions = [], bypassLocal = true) {
return new Promise((resolve) => {
const config = this.createProxyConfig(host, port, exceptions, bypassLocal);
chrome.proxy.settings.set({ value: config, scope: 'regular' }, () => {
if (chrome.runtime.lastError) {
Utils.log('error', 'Failed to apply proxy', chrome.runtime.lastError);
resolve(false);
} else {
Utils.log('info', `Proxy applied: ${host}:${port}`);
this._currentProxy = { host, port };
this._isEnabled = true;
resolve(true);
}
});
});
},
/**
* Clear proxy settings
* @returns {Promise<boolean>}
*/
async clearProxy() {
return new Promise((resolve) => {
chrome.proxy.settings.clear({}, () => {
if (chrome.runtime.lastError) {
Utils.log('error', 'Failed to clear proxy', chrome.runtime.lastError);
resolve(false);
} else {
Utils.log('info', 'Proxy cleared');
this._currentProxy = null;
this._isEnabled = false;
this._currentIndex = 0;
resolve(true);
}
});
});
},
/**
* Test proxy connectivity
* @param {object} proxy - Proxy object {host, port}
* @returns {Promise<{working: boolean, latency: number|null}>}
*/
async testProxy(proxy) {
Utils.log('debug', `Testing proxy ${proxy.host}:${proxy.port}`);
// First, set the proxy
const config = this.createProxyConfig(proxy.host, proxy.port);
return new Promise((resolve) => {
chrome.proxy.settings.set({ value: config, scope: 'regular' }, async () => {
if (chrome.runtime.lastError) {
Utils.log('error', 'Failed to set proxy for test', chrome.runtime.lastError);
resolve({ working: false, latency: null });
return;
}
// Longer delay to ensure proxy settings are applied
await new Promise(r => setTimeout(r, 300));
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), CONFIG.TIMEOUTS.PROXY_CHECK);
// Measure latency only for the actual request
const startTime = Date.now();
// Use HEAD request with cache disabled for reliable test
const response = await fetch(CONFIG.URLS.CHECK_IP, {
method: 'HEAD',
cache: 'no-store',
signal: controller.signal
});
const latency = Date.now() - startTime;
clearTimeout(timeoutId);
if (response.ok || response.type === 'opaque') {
Utils.log('debug', `Proxy ${proxy.host}:${proxy.port} working, latency: ${latency}ms`);
resolve({ working: true, latency });
} else {
Utils.log('debug', `Proxy ${proxy.host}:${proxy.port} returned ${response.status}`);
// Clear proxy settings on failure
chrome.proxy.settings.clear({});
resolve({ working: false, latency: null });
}
} catch (error) {
Utils.log('debug', `Proxy ${proxy.host}:${proxy.port} failed: ${error.name} - ${error.message}`);
// Clear proxy settings on failure
chrome.proxy.settings.clear({});
resolve({ working: false, latency: null });
}
});
});
},
/**
* Test multiple proxies in parallel
* @param {object[]} proxies - Array of proxy objects
* @param {number} concurrency - Max concurrent tests
* @returns {Promise<object[]>}
*/
async testProxiesParallel(proxies, concurrency = 5) {
const results = [];
const chunks = [];
// Split into chunks
for (let i = 0; i < proxies.length; i += concurrency) {
chunks.push(proxies.slice(i, i + concurrency));
}
// Process chunks sequentially, proxies within chunk in parallel
for (const chunk of chunks) {
const chunkResults = await Promise.all(
chunk.map(async (proxy) => {
const result = await this.testProxy(proxy);
return { ...proxy, ...result };
})
);
results.push(...chunkResults);
}
return results;
},
/**
* Find and connect to fastest working proxy
* @param {string} country - Country filter (optional)
* @param {boolean} bypassLocal - Allow local network access
* @param {string[]} exceptions - Bypass list
* @returns {Promise<{success: boolean, proxy: object|null, error: string|null}>}
*/
async connectToFastest(country = null, bypassLocal = true, exceptions = []) {
Utils.log('info', 'Finding fastest proxy', { country, exceptions });
let proxies = this._proxyList;
// Test proxies in parallel
const results = await this.testProxiesParallel(proxies);
// Filter working proxies and sort by latency
const working = results
.filter(p => p.working)
.sort((a, b) => a.latency - b.latency);
if (working.length === 0) {
return { success: false, proxy: null, error: 'No working proxies found' };
}
// Connect to fastest
const fastest = working[0];
const applied = await this.applyProxy(fastest.host, fastest.port, exceptions, bypassLocal);
if (applied) {
await Storage.setCurrentProxy(fastest);
await Storage.setProxyEnabled(true);
return { success: true, proxy: fastest, error: null };
}
return { success: false, proxy: null, error: 'Failed to apply proxy settings' };
},
/**
* Connect using custom proxy
* @param {string} ip - Proxy IP
* @param {number} port - Proxy port
* @param {string[]} exceptions - Bypass list
* @param {boolean} bypassLocal - Allow local network access
* @returns {Promise<{success: boolean, error: string|null}>}
*/
async connectCustom(ip, port, exceptions = [], bypassLocal = true) {
// Validate
const validation = Utils.validateProxy(ip, port);
if (!validation.valid) {
return { success: false, error: validation.error };
}
// Test connection
const testResult = await this.testProxy({ host: ip, port });
if (!testResult.working) {
return { success: false, error: 'Proxy is not responding' };
}
// Apply
const applied = await this.applyProxy(ip, port, exceptions, bypassLocal);
if (applied) {
const proxy = { host: ip, port, type: 'custom' };
// Get existing credentials to preserve them when updating storage
const existingProxy = await Storage.getCustomProxy();
await Storage.setCustomProxy(ip, port, exceptions, existingProxy.username, existingProxy.password);
await Storage.setCurrentProxy(proxy);
await Storage.setProxyEnabled(true);
return { success: true, proxy, error: null };
}
return { success: false, proxy: null, error: 'Failed to apply proxy settings' };
},
/**
* Disconnect proxy
* @returns {Promise<boolean>}
*/
async disconnect() {
const cleared = await this.clearProxy();
if (cleared) {
await Storage.setProxyEnabled(false);
await Storage.setCurrentProxy(null);
}
return cleared;
},
/**
* Fetch proxy list from source with fallback
* @param {string} source - Source key from CONFIG.PROXY_SOURCES
* @returns {Promise<{success: boolean, proxies: object[], error: string|null, usedSource: string|null}>}
*/
async fetchProxyList(source = 'arweave') {
// Define fallback order
const fallbackOrder = ['arweave', 'git', 'github'];
// Start with the requested source, then try others
const sourcesToTry = [source, ...fallbackOrder.filter(s => s !== source)];
for (const currentSource of sourcesToTry) {
const sourceConfig = CONFIG.PROXY_SOURCES[currentSource];
if (!sourceConfig) continue;
Utils.log('info', `Fetching proxies from ${currentSource}`);
try {
const response = await Utils.fetchWithTimeout(
sourceConfig.url,
{},
CONFIG.TIMEOUTS.FETCH
);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
// Normalize data format
let proxies = Array.isArray(data) ? data : data.proxies || [];
// Ensure each proxy has required fields
proxies = proxies.map(p => ({
host: p.host || p.ip,
port: parseInt(p.port, 10),
country: p.country || null,
country_name: p.country_name || null,
city: p.city || null,
latency: null
}));
this._proxyList = proxies;
await Storage.setProxyList(proxies);
if (currentSource !== source) {
Utils.log('info', `Fallback: fetched ${proxies.length} proxies from ${currentSource} (${source} failed)`);
} else {
Utils.log('info', `Fetched ${proxies.length} proxies from ${currentSource}`);
}
return { success: true, proxies, error: null, usedSource: currentSource };
} catch (error) {
Utils.log('warn', `Failed to fetch from ${currentSource}: ${error.message}`);
// Continue to next source
}
}
// All sources failed
Utils.log('error', 'All proxy sources failed');
return { success: false, proxies: [], error: 'All proxy sources failed', usedSource: null };
},
/**
* Fallback to next proxy
* @returns {Promise<{success: boolean, proxy: object|null, error: string|null}>}
*/
async fallbackToNext() {
if (this._proxyList.length === 0) {
return { success: false, proxy: null, error: 'No proxies available. Please refresh the proxy list.' };
}
if (this._proxyList.length === 1) {
return { success: false, proxy: null, error: 'Only one proxy available' };
}
this._currentIndex = (this._currentIndex + 1) % this._proxyList.length;
const proxy = this._proxyList[this._currentIndex];
Utils.log('info', `Switching to proxy ${this._currentIndex + 1}/${this._proxyList.length}: ${proxy.host}:${proxy.port}`);
// Test the proxy first to get latency
const testResult = await this.testProxy(proxy);
if (!testResult.working) {
// Try next proxy if this one doesn't work
Utils.log('warn', `Proxy ${proxy.host}:${proxy.port} not working, trying next...`);
return this.fallbackToNext();
}
// Merge test results (latency) with proxy info
const proxyWithLatency = { ...proxy, latency: testResult.latency };
// Get bypass local setting and exceptions
const bypassLocal = await Storage.getValue(CONFIG.STORAGE_KEYS.BYPASS_LOCAL, true);
const exceptions = await Storage.getValue(CONFIG.STORAGE_KEYS.EXCEPTIONS, []);
const applied = await this.applyProxy(proxy.host, proxy.port, exceptions, bypassLocal);
if (applied) {
await Storage.setCurrentProxy(proxyWithLatency);
return { success: true, proxy: proxyWithLatency, error: null };
}
return { success: false, proxy: null, error: 'Failed to apply proxy settings' };
},
/**
* Get current status
* @returns {object}
*/
getStatus() {
return {
enabled: this._isEnabled,
currentProxy: this._currentProxy,
proxyCount: this._proxyList.length
};
}
};
// ES Module export
export { ProxyManager };

257
js/storage.js Normal file
View File

@ -0,0 +1,257 @@
/* ANyONe Extension v2 - Storage Manager */
import { CONFIG } from './config.js';
const Storage = {
/**
* Get value from storage
* @param {string|string[]} keys - Key(s) to retrieve
* @returns {Promise<object>}
*/
async get(keys) {
return new Promise((resolve, reject) => {
try {
chrome.storage.local.get(keys, (result) => {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
} else {
resolve(result);
}
});
} catch (error) {
reject(error);
}
});
},
/**
* Set value in storage
* @param {object} data - Data to store
* @returns {Promise<void>}
*/
async set(data) {
return new Promise((resolve, reject) => {
try {
chrome.storage.local.set(data, () => {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
} else {
resolve();
}
});
} catch (error) {
reject(error);
}
});
},
/**
* Remove keys from storage
* @param {string|string[]} keys - Key(s) to remove
* @returns {Promise<void>}
*/
async remove(keys) {
return new Promise((resolve, reject) => {
try {
chrome.storage.local.remove(keys, () => {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
} else {
resolve();
}
});
} catch (error) {
reject(error);
}
});
},
/**
* Clear all storage
* @returns {Promise<void>}
*/
async clear() {
return new Promise((resolve, reject) => {
try {
chrome.storage.local.clear(() => {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
} else {
resolve();
}
});
} catch (error) {
reject(error);
}
});
},
/**
* Get single value with default
* @param {string} key - Key to retrieve
* @param {*} defaultValue - Default value if not found
* @returns {Promise<*>}
*/
async getValue(key, defaultValue = null) {
const result = await this.get(key);
return result[key] !== undefined ? result[key] : defaultValue;
},
/**
* Set single value
* @param {string} key - Key to set
* @param {*} value - Value to store
* @returns {Promise<void>}
*/
async setValue(key, value) {
return this.set({ [key]: value });
},
// ============================================
// Convenience methods for common operations
// ============================================
/**
* Get current connection mode
* @returns {Promise<string>}
*/
async getMode() {
return this.getValue(CONFIG.STORAGE_KEYS.MODE, CONFIG.DEFAULTS.MODE);
},
/**
* Set connection mode
* @param {string} mode - Mode to set
* @returns {Promise<void>}
*/
async setMode(mode) {
return this.setValue(CONFIG.STORAGE_KEYS.MODE, mode);
},
/**
* Check if proxy is enabled
* @returns {Promise<boolean>}
*/
async isProxyEnabled() {
return this.getValue(CONFIG.STORAGE_KEYS.PROXY_ENABLED, false);
},
/**
* Set proxy enabled state
* @param {boolean} enabled - Enabled state
* @returns {Promise<void>}
*/
async setProxyEnabled(enabled) {
return this.setValue(CONFIG.STORAGE_KEYS.PROXY_ENABLED, enabled);
},
/**
* Get proxy list
* @returns {Promise<Array>}
*/
async getProxyList() {
return this.getValue(CONFIG.STORAGE_KEYS.PROXY_LIST, []);
},
/**
* Set proxy list
* @param {Array} list - Proxy list
* @returns {Promise<void>}
*/
async setProxyList(list) {
await this.set({
[CONFIG.STORAGE_KEYS.PROXY_LIST]: list,
[CONFIG.STORAGE_KEYS.LAST_UPDATE]: Date.now()
});
},
/**
* Get current proxy
* @returns {Promise<object|null>}
*/
async getCurrentProxy() {
return this.getValue(CONFIG.STORAGE_KEYS.CURRENT_PROXY, null);
},
/**
* Set current proxy
* @param {object} proxy - Proxy object
* @returns {Promise<void>}
*/
async setCurrentProxy(proxy) {
return this.setValue(CONFIG.STORAGE_KEYS.CURRENT_PROXY, proxy);
},
/**
* Get custom proxy settings
* @returns {Promise<{ip: string, port: number, username: string, password: string, exceptions: string[]}>}
*/
async getCustomProxy() {
const result = await this.get([
CONFIG.STORAGE_KEYS.CUSTOM_IP,
CONFIG.STORAGE_KEYS.CUSTOM_PORT,
CONFIG.STORAGE_KEYS.CUSTOM_USERNAME,
CONFIG.STORAGE_KEYS.CUSTOM_PASSWORD,
CONFIG.STORAGE_KEYS.EXCEPTIONS
]);
return {
ip: result[CONFIG.STORAGE_KEYS.CUSTOM_IP] || '',
port: result[CONFIG.STORAGE_KEYS.CUSTOM_PORT] || '',
username: result[CONFIG.STORAGE_KEYS.CUSTOM_USERNAME] || '',
password: result[CONFIG.STORAGE_KEYS.CUSTOM_PASSWORD] || '',
exceptions: result[CONFIG.STORAGE_KEYS.EXCEPTIONS] || []
};
},
/**
* Set custom proxy settings
* @param {string} ip - IP address
* @param {number} port - Port number
* @param {string[]} exceptions - Exception list
* @param {string} username - Proxy username (optional)
* @param {string} password - Proxy password (optional)
* @returns {Promise<void>}
*/
async setCustomProxy(ip, port, exceptions = [], username = '', password = '') {
return this.set({
[CONFIG.STORAGE_KEYS.CUSTOM_IP]: ip,
[CONFIG.STORAGE_KEYS.CUSTOM_PORT]: port,
[CONFIG.STORAGE_KEYS.CUSTOM_USERNAME]: username,
[CONFIG.STORAGE_KEYS.CUSTOM_PASSWORD]: password,
[CONFIG.STORAGE_KEYS.EXCEPTIONS]: exceptions
});
},
/**
* Clear custom proxy settings
* @returns {Promise<void>}
*/
async clearCustomProxy() {
return this.remove([
CONFIG.STORAGE_KEYS.CUSTOM_IP,
CONFIG.STORAGE_KEYS.CUSTOM_PORT,
CONFIG.STORAGE_KEYS.CUSTOM_USERNAME,
CONFIG.STORAGE_KEYS.CUSTOM_PASSWORD
]);
},
/**
* Get all settings for options page
* @returns {Promise<object>}
*/
async getAllSettings() {
const keys = Object.values(CONFIG.STORAGE_KEYS);
return this.get(keys);
},
/**
* Get last proxy update timestamp
* @returns {Promise<number|null>}
*/
async getLastUpdate() {
return this.getValue(CONFIG.STORAGE_KEYS.LAST_UPDATE, null);
}
};
// ES Module export
export { Storage };

View File

@ -1,27 +0,0 @@
document.addEventListener("DOMContentLoaded", () => {
const comingSoonText = document.getElementById("comingSoon");
const comingSoonImage = document.getElementById("fullScreenImage");
// Check if the domain is working
fetch('https://dapps.debros.io', { method: 'HEAD', mode: 'no-cors' })
.then(response => {
if (response.ok) {
window.location.href = 'https://dapps.debros.io';
} else {
showComingSoonMessage();
}
})
.catch(() => {
showComingSoonMessage();
});
function showComingSoonMessage() {
setTimeout(() => {
comingSoonImage.style.opacity = 1;
}, 200);
setTimeout(() => {
comingSoonText.style.opacity = 1;
}, 1000);
}
});

273
js/utils.js Normal file
View File

@ -0,0 +1,273 @@
/* ANyONe Extension v2 - Utility Functions */
const Utils = {
/**
* Validate IP address format
* @param {string} ip - IP address to validate
* @returns {boolean}
*/
isValidIP(ip) {
if (!ip || typeof ip !== 'string') return false;
const ipRegex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
return ipRegex.test(ip.trim());
},
/**
* Validate hostname/domain format
* @param {string} hostname - Hostname to validate
* @returns {boolean}
*/
isValidHostname(hostname) {
if (!hostname || typeof hostname !== 'string') return false;
const trimmed = hostname.trim();
// Must have at least one dot for a valid domain
if (!trimmed.includes('.')) return false;
// Split by dots and validate each part
const parts = trimmed.split('.');
for (let i = 0; i < parts.length; i++) {
const part = parts[i];
// Each part must be 1-63 chars
if (part.length === 0 || part.length > 63) return false;
// Only alphanumeric and hyphens allowed
if (!/^[A-Za-z0-9-]+$/.test(part)) return false;
// Cannot start or end with hyphen
if (part.startsWith('-') || part.endsWith('-')) return false;
}
// TLD must be at least 2 letters (no numbers)
const tld = parts[parts.length - 1];
if (!/^[A-Za-z]{2,}$/.test(tld)) return false;
return true;
},
/**
* Validate host (IP or hostname)
* @param {string} host - Host to validate (IP or hostname)
* @returns {boolean}
*/
isValidHost(host) {
return this.isValidIP(host) || this.isValidHostname(host);
},
/**
* Validate port number
* @param {number|string} port - Port to validate
* @returns {boolean}
*/
isValidPort(port) {
const portNum = parseInt(port, 10);
return !isNaN(portNum) && portNum >= 1 && portNum <= 65535;
},
/**
* Validate proxy configuration
* @param {string} host - IP address or hostname
* @param {number|string} port - Port number
* @returns {{valid: boolean, error?: string}}
*/
validateProxy(host, port) {
if (!this.isValidHost(host)) {
return { valid: false, error: 'Invalid IP address or hostname' };
}
if (!this.isValidPort(port)) {
return { valid: false, error: 'Port must be between 1 and 65535' };
}
return { valid: true };
},
/**
* Format bytes to human readable size
* @param {number} bytes - Bytes to format
* @returns {string}
*/
formatBytes(bytes) {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
},
/**
* Format duration in seconds to human readable
* @param {number} seconds - Duration in seconds
* @returns {string}
*/
formatDuration(seconds) {
const hrs = Math.floor(seconds / 3600);
const mins = Math.floor((seconds % 3600) / 60);
const secs = seconds % 60;
return [hrs, mins, secs]
.map(v => v.toString().padStart(2, '0'))
.join(':');
},
/**
* Format timestamp to relative time
* @param {number} timestamp - Unix timestamp
* @returns {string}
*/
formatRelativeTime(timestamp) {
const now = Date.now();
const diff = now - timestamp;
const minutes = Math.floor(diff / 60000);
const hours = Math.floor(diff / 3600000);
const days = Math.floor(diff / 86400000);
if (minutes < 1) return 'Just now';
if (minutes < 60) return `${minutes} minute${minutes > 1 ? 's' : ''} ago`;
if (hours < 24) return `${hours} hour${hours > 1 ? 's' : ''} ago`;
return `${days} day${days > 1 ? 's' : ''} ago`;
},
/**
* Parse exceptions string to array
* @param {string} exceptions - Comma or newline separated exceptions
* @returns {string[]}
*/
parseExceptions(exceptions) {
if (!exceptions || typeof exceptions !== 'string') return [];
return exceptions
.split(/[,\n]/)
.map(e => e.trim())
.filter(e => e.length > 0);
},
/**
* Format exceptions array to string
* @param {string[]} exceptions - Array of exceptions
* @returns {string}
*/
formatExceptions(exceptions) {
if (!Array.isArray(exceptions)) return '';
return exceptions.join(', ');
},
/**
* Debounce function calls
* @param {Function} func - Function to debounce
* @param {number} wait - Wait time in ms
* @returns {Function}
*/
debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
},
/**
* Create a promise that rejects after timeout
* @param {number} ms - Timeout in milliseconds
* @returns {Promise}
*/
timeout(ms) {
return new Promise((_, reject) => {
setTimeout(() => reject(new Error('Timeout')), ms);
});
},
/**
* Fetch with timeout
* @param {string} url - URL to fetch
* @param {object} options - Fetch options
* @param {number} timeoutMs - Timeout in ms
* @returns {Promise<Response>}
*/
async fetchWithTimeout(url, options = {}, timeoutMs = 10000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(url, {
...options,
signal: controller.signal
});
clearTimeout(timeoutId);
return response;
} catch (error) {
clearTimeout(timeoutId);
throw error;
}
},
/**
* Generate unique ID
* @returns {string}
*/
generateId() {
return Date.now().toString(36) + Math.random().toString(36).substr(2);
},
/**
* Safely parse JSON
* @param {string} json - JSON string
* @param {*} fallback - Fallback value on error
* @returns {*}
*/
safeParseJSON(json, fallback = null) {
try {
return JSON.parse(json);
} catch {
return fallback;
}
},
/**
* Deep clone object
* @param {*} obj - Object to clone
* @returns {*}
*/
deepClone(obj) {
return JSON.parse(JSON.stringify(obj));
},
/**
* Check if running in extension context
* @returns {boolean}
*/
isExtensionContext() {
return typeof chrome !== 'undefined' && chrome.runtime && chrome.runtime.id;
},
/**
* Log with prefix
* @param {string} level - Log level
* @param {string} message - Message
* @param {*} data - Optional data
*/
log(level, message, data = null) {
const prefix = '[ANyONe]';
const timestamp = new Date().toISOString();
const logMessage = `${prefix} ${timestamp} [${level.toUpperCase()}] ${message}`;
switch (level) {
case 'error':
console.log(logMessage, data || '');
break;
case 'warn':
console.warn(logMessage, data || '');
break;
case 'debug':
console.debug(logMessage, data || '');
break;
default:
console.log(logMessage, data || '');
}
}
};
// ES Module export
export { Utils };

View File

@ -1,21 +1,30 @@
{
"manifest_version": 3,
"name": "ANyONe Extension",
"version": "1.0.5",
"description": "Manage Socks5 proxy settings",
"version": "2.0.0",
"description": "Privacy-focused Socks5 proxy management",
"permissions": [
"proxy",
"storage",
"tabs",
"scripting"
"scripting",
"privacy",
"webRequest",
"webRequestAuthProvider"
],
"host_permissions": ["<all_urls>"],
"background": {
"service_worker": "js/background.js"
"service_worker": "js/background.js",
"type": "module"
},
"action": {
"default_popup": "html/popup.html",
"default_title": "Proxy Settings"
"default_title": "ANyONe - Privacy Extension",
"default_icon": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
},
"options_ui": {
"page": "html/options.html",