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

258
README.md
View File

@ -1,181 +1,165 @@
# ANyONe Extension - Manage Socks5 Proxy Settings # ANyONe Extension v2.0
## Overview A privacy-focused Chromium browser extension for managing SOCKS5 proxy connections to the ANyONe network.
**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>
## Features ## Features
### 1. Quick Proxy Toggle ### Connection Modes
- **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.
### 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. #### Custom Proxy Mode
- **Host and Port**: Specify the host IP and port number for your custom proxy. - **Full SOCKS5 configuration** with IP/hostname and port
- **No Proxy Exceptions**: Define specific websites or local addresses where the proxy should not be applied. - **Authentication support** for proxies requiring username/password
- **Detailed Configuration**: The options page allows for: - **Test connection** before connecting
- Setting up custom proxy configurations.
- Managing exceptions to proxy use.
- Saving and applying changes for a tailored browsing experience.
### 3. Proxy Status Indication ### Privacy & Security
- **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.
### 4. External Links | Feature | Description |
- **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. | **WebRTC Leak Protection** | Prevents real IP leaks through WebRTC |
- **Popup**: Links to the developer's website, X account, GitHub repository for the extension, and ANyONe website. | **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 - **Auto-connect on startup** - Automatically connect when browser starts
- Click the extension icon to open the popup. - **Default connection mode** - Choose Public or Custom as default
- Use the toggle switch to enable or disable the proxy. - **Proxy source selection** - Arweave (default), GitBros, or GitHub with automatic fallback
- The status message will update to reflect the current state. - **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. - **Modern dark theme** with clean, intuitive design
- **Update Mechanism**: You can easily refresh the proxy list directly within the extension using an update button. - **Real-time status** showing connection state and current proxy
- **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). - **Quick actions** - Check IP, refresh proxies, access settings
- **Toast notifications** for instant feedback
### 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.
## Installation ## Installation
### 1. Clone or Download the Repository ### Option 1: Clone with Git
You have two options to get the extension on your system: ```bash
git clone "https://git.debros.io/DeBros/anyone-extension.git"
```
- **Option A: Clone with Git** ### Option 2: Download ZIP
```bash [Download ZIP](https://git.debros.io/DeBros/anyone-extension/archive/main.zip)
git clone "https://git.debros.io/DeBros/anyone-extension.git"
```
- **Option B: [Download ZIP](https://git.debros.io/DeBros/anyone-extension/archive/main.zip)** ### 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`
### 2. Load Unpacked Extension in Chromium-based Browser ## Usage
- 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.
## Contribution
Contributions are welcome! Please fork the repository and submit pull requests for any enhancements or bug fixes.
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
# Optional: Enhancing Security with Custom DNS Configuration ### 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
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: ### 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**
## Cloudflare DNS (1.1.1.1) ### Check Your Connection
Click **Check IP** to verify your connection is routed through the ANyONe network.
- **IPv4**: 1.1.1.1 and 1.0.0.1 ## Proxy Sources
- **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) The extension fetches proxy lists from multiple sources with automatic fallback:
- **IPv4**: 9.9.9.9 and 149.112.112.112 | Source | URL | Type |
- **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. | **Arweave** (Default) | [arweave.net/FjxfWIbS...](https://arweave.net/FjxfWIbSnZb7EaJWbeuWCsBBFWjTppfS3_KHxUP__B8) | Decentralized, permanent |
- **Website**: [https://quad9.net](https://quad9.net) | **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 |
## Mullvad DNS **Fallback order:** Arweave → GitBros → GitHub
- **IPv4**: 194.242.2.1 and 194.242.2.2 ## Privacy Settings Explained
- **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 ### WebRTC Leak Protection
WebRTC can expose your real IP even when using a proxy. Enable this to prevent leaks.
- **IPv4**: 94.140.14.14 and 94.140.15.15 (without filters), 176.103.130.130 and 176.103.130.131 (with filters) ### Kill Switch
- **IPv6**: 2a10:50c0::ad1:ff and 2a10:50c0::ad2:ff (without filters), 2a10:50c0::bad1:ff and 2a10:50c0::bad2:ff (with filters) 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.
- **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: ### 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
- **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. ## Technical Details
- **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. - **Manifest V3** compliant
- **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. - **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).
--- ---
# ANyONe Protocol: Connection and Setup Guide ## Optional: Secure DNS Configuration
**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. Enhance your privacy by using secure DNS servers:
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: ### Recommended DNS Providers
- **Linux**: Enjoy a seamless one-click setup. Learn more in the [Linux Connection Guide](https://docs.anyone.io/connect/connecting-to-linux). | Provider | IPv4 | Features |
- **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). | **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 |
**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). | **Mullvad** | 194.242.2.2 | Full privacy, no logging |
| **AdGuard** | 94.140.14.14, 94.140.15.15 | Ad blocking, no logging |
**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.
--- ---
### ## ANyONe Protocol Resources
<br clear="both"> - [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)
---
<div align="center"> <div align="center">
<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>
<a href="https://x.com/debrosofficial" target="_blank">
<img src="https://img.shields.io/static/v1?message=Twitter&logo=twitter&label=&color=1DA1F2&logoColor=white&labelColor=&style=for-the-badge" height="35" alt="twitter logo" />
</a>
<a href="https://t.me/debrosportal" target="_blank">
<img src="https://img.shields.io/static/v1?message=Telegram&logo=telegram&label=&color=2CA5E0&logoColor=white&labelColor=&style=for-the-badge" height="35" alt="telegram logo" />
</a>
<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>
### **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>
<a href="https://x.com/debrosofficial" target="_blank">
<img src="https://img.shields.io/static/v1?message=Twitter&logo=twitter&label=&color=1DA1F2&logoColor=white&labelColor=&style=for-the-badge" height="35" alt="twitter logo" />
</a>
<a href="https://t.me/debrosportal" target="_blank">
<img src="https://img.shields.io/static/v1?message=Telegram&logo=telegram&label=&color=2CA5E0&logoColor=white&labelColor=&style=for-the-badge" height="35" alt="telegram logo" />
</a>
<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>

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> <!DOCTYPE html>
<html> <html lang="en">
<head> <head>
<title>Proxy Settings</title> <meta charset="UTF-8">
<style> <meta name="viewport" content="width=device-width, initial-scale=1.0">
* { <title>ANyONe Extension Settings</title>
box-sizing: border-box; <link rel="stylesheet" href="../css/options.css">
} </head>
<body>
<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>
body { <!-- General Settings -->
font-family: Arial, Helvetica, sans-serif; <section class="settings-section">
margin: 0; <div class="section-title">General</div>
text-align: center; <div class="settings-card">
color: #ffffff; <div class="setting-row">
background-image: url('../images/optionsback.png'); <div class="setting-info">
background-size: cover; <div class="setting-label">Auto-connect on startup</div>
background-position: center; <div class="setting-desc">Automatically connect when browser starts</div>
background-repeat: no-repeat; </div>
background-attachment: fixed; <div class="setting-control">
min-height: 100vh; <label class="toggle">
overflow-y: hidden; <input type="checkbox" id="auto-connect">
display: flex; <span class="toggle-slider"></span>
justify-content: center; </label>
align-items: center; </div>
} </div>
h1 { <div class="setting-row">
margin-top: 20px; <div class="setting-info">
white-space: nowrap; <div class="setting-label">Default connection mode</div>
font-size: 24px; <div class="setting-desc">Mode to use when auto-connecting</div>
} </div>
<div class="setting-control">
.container { <select class="select" id="default-mode">
width: 400px; <option value="public">Public Proxies</option>
margin: 15px auto; <option value="custom">Custom Proxy</option>
padding: 20px; </select>
background-color: rgba(0, 0, 0, 0.6); </div>
border-radius: 10px; </div>
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>
</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>
<label for="proxyPort">
Port
<input type="number" id="proxyPort" placeholder="port" aria-label="Proxy Port">
</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>
</section>
<div class="button-group"> <!-- Privacy Settings -->
<button id="saveSettings">Save & Enable</button> <section class="settings-section">
<button id="disableProxy">Disable</button> <div class="section-title">Privacy</div>
<button id="checkAnyoneButton">Check ANyONe</button> <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>
<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> </div>
<p id="statusMessage"></p> </section>
<div class="credits"> <!-- Custom Proxy Settings -->
<a href="https://debros.io" target="_blank" rel="noopener noreferrer" class="credits-link" aria-label="Visit DeBros website"> <section class="settings-section">
<img src="../images/debroslogo.png" alt="DeBros Logo" width="30" height="30"> <div class="section-title">Custom Proxy</div>
<span>This extension was created by DeBros</span> <div class="settings-card">
</a> <div class="setting-row vertical">
<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"> <div class="setting-info">
<span>It's open source - explore the code and docs here</span> <div class="setting-label">Proxy Address</div>
</a> <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>
<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> </div>
<script src="../js/options.js"></script> </div>
</body>
</html> <!-- 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> <!DOCTYPE html>
<html> <html lang="en">
<head> <head>
<title>ANyONe Network Gateway</title> <meta charset="UTF-8">
<style> <meta name="viewport" content="width=device-width, initial-scale=1.0">
body { <title>ANyONe Extension</title>
font-family: Arial, Helvetica, sans-serif; <link rel="stylesheet" href="../css/popup.css">
margin: 0; </head>
padding: 10px 30px 10px; <body>
background-image: url('../images/popupback.png'); <div class="popup-container">
background-size: cover; <!-- Header -->
background-repeat: no-repeat; <header class="popup-header">
background-position: center; <div class="logo">
background-color: #6d8392; <img src="../images/anyone2.png" alt="ANyONe" class="logo-img">
color: white; <span class="logo-subtitle">Privacy Extension</span>
text-align: center; </div>
width: 220px; </header>
height: 500px;
overflow: hidden;
}
#anonLogo { <!-- Mode Selection -->
margin: 10px auto 10px; <section class="mode-section">
display: block; <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>
.switch { <!-- Mode Content: Public -->
position: relative; <section class="mode-content active" id="mode-public">
display: inline-block; <div class="card">
width: 50px; <div class="proxy-info">
height: 15px; <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>
.switch input { <!-- Mode Content: Custom -->
opacity: 1; <section class="mode-content" id="mode-custom">
width: 0; <div class="card">
height: 0; <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>
.slider { <!-- Connect Button -->
position: absolute; <section class="connect-section">
cursor: pointer; <button class="connect-btn" id="btn-connect">
top: 0; <div class="connect-btn-ring"></div>
left: 0; <img src="../images/anonlogo.png" alt="Connect" class="connect-btn-icon">
right: 0; </button>
bottom: 0; </section>
background-color: #e74c3c;
transition: 0.4s;
border-radius: 34px;
}
.slider:before { <!-- Status Card -->
position: absolute; <section class="status-card" id="status-card">
content: ""; <div class="status-main">
height: 25px; <div class="status-dot-container">
width: 25px; <span class="status-dot offline" id="status-dot"></span>
left: 0px; </div>
bottom: -4px; <div class="status-info">
background-color: #ffffff; <span class="status-text" id="status-text">Disconnected</span>
transition: 0.4s; <span class="status-ip" id="status-ip" style="display: none;">-</span>
border-radius: 50%; </div>
} </div>
</section>
input:checked + .slider { <!-- Quick Actions -->
background-color: #02af50; <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>
input:checked + .slider:before { <script type="module" src="../js/popup.js"></script>
transform: translateX(26px); </body>
} </html>
#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>
</head>
<body>
<img src="../images/anonlogo.png" alt="AnON Logo" width="120" height="120" id="anonLogo">
<h1>ANyONe Proxy</h1>
<label class="switch">
<input type="checkbox" id="proxyToggle">
<span class="slider"></span>
</label>
<p id="statusMessage"></p>
<div class="button-container-1">
<button id="checkAnyoneButton">Check ANyONe</button>
<button id="updateProxiesButton">Update Proxies</button>
</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>
</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 /* ANyONe Extension v2 - Background Service Worker */
let currentProxyIndex = 0; // Start with the first proxy
let proxyEnabled = false;
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 = { // Initialization
mode: "fixed_servers", // ============================================
rules: {
singleProxy: {
scheme: "socks5",
host,
port,
},
bypassList: exceptions.concat([""]),
},
};
chrome.proxy.settings.set({ value: proxyConfig, scope: "regular" }, () => { Utils.log('info', 'Background service worker starting...');
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;
}
});
}
function clearProxySettings() { // Initialize on startup
chrome.proxy.settings.clear({}, () => { chrome.runtime.onInstalled.addListener(async (details) => {
const lastError = chrome.runtime.lastError; Utils.log('info', `Extension ${details.reason}`, { version: CONFIG.VERSION });
if (lastError) {
console.error("Error clearing proxy settings:", lastError);
} else {
console.log("Proxy settings cleared.");
proxyEnabled = false;
currentProxyIndex = 0; // Reset to the first proxy
}
});
}
function fallbackToNextProxy() { if (details.reason === 'install') {
currentProxyIndex = (currentProxyIndex + 1) % proxies.length; // Move to the next proxy in the list // First install
if (proxies.length > 0) { Utils.log('info', 'First install, setting up defaults...');
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) { // Set defaults
const proxyConfig = { await Storage.set({
mode: "fixed_servers", [CONFIG.STORAGE_KEYS.MODE]: CONFIG.DEFAULTS.MODE,
rules: { [CONFIG.STORAGE_KEYS.PROXY_ENABLED]: false,
singleProxy: { [CONFIG.STORAGE_KEYS.AUTO_CONNECT]: CONFIG.DEFAULTS.AUTO_CONNECT,
scheme: "socks5", [CONFIG.STORAGE_KEYS.WEBRTC_PROTECTION]: CONFIG.DEFAULTS.WEBRTC_PROTECTION,
host: proxy.host, [CONFIG.STORAGE_KEYS.BYPASS_LOCAL]: CONFIG.DEFAULTS.BYPASS_LOCAL
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(); // Auto-fetch proxies on first install
} Utils.log('info', 'Fetching initial proxy list...');
try {
function fetchProxies() { const source = CONFIG.DEFAULTS.PROXY_SOURCE;
console.log("Starting to fetch proxies..."); await handleFetchProxies(source);
return fetch('https://git.debros.io/DeBros/anyone-proxy-list/raw/branch/main/anonproxies.json') // list of public proxies Utils.log('info', 'Initial proxy list fetched successfully');
.then(response => { } catch (error) {
console.log("Response status:", response.status); Utils.log('error', 'Failed to fetch initial proxy list', error);
if (!response.ok) { }
console.error("Response not OK"); } else if (details.reason === 'update') {
throw new Error('Network response was not ok'); Utils.log('info', 'Extension updated');
}
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.');
} }
}); });
// This event listener runs when the extension is first installed or updated // Load state on startup
chrome.runtime.onInstalled.addListener((details) => { chrome.runtime.onStartup.addListener(async () => {
if (details.reason === "install") { Utils.log('info', 'Browser startup');
console.log("Extension installed for the first time. Fetching proxies..."); await ProxyManager.init();
// Fetch proxies automatically on first install but don't enable them
fetchProxies().then(() => { // Check auto-connect setting
console.log("Proxies updated on first install."); const autoConnect = await Storage.getValue(CONFIG.STORAGE_KEYS.AUTO_CONNECT, false);
// Don't enable proxy automatically, just fetch and store it 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) => { chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
console.log("Received message in background:", message); Utils.log('debug', 'Message received', message);
if (message.action === "enableProxy") {
console.log("Enabling proxy..."); // Handle async responses
chrome.runtime.sendMessage({ action: "showLoadingMessage", message: "Please wait..." }); handleMessage(message, sender)
enableFirstWorkingProxy((success) => { .then(response => sendResponse(response))
if (success) { .catch(error => {
console.log("Proxy enabled successfully"); Utils.log('error', 'Message handler error', error);
sendResponse({ status: "enabled", proxy: proxies[currentProxyIndex] }); sendResponse({ success: false, error: error.message });
} 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." });
}
}); });
return true; // For async response
} else if (message.action === "disableProxy") { return true; // Keep channel open for async response
console.log("Disabling proxy..."); });
clearProxySettings();
chrome.storage.local.set({ proxyEnabled: false, proxyType: null }); /**
sendResponse({ status: "disabled" }); * Handle incoming messages
chrome.tabs.query({}, function(tabs) { * @param {object} message - Message object
for (let tab of tabs) { * @param {object} sender - Sender info
chrome.tabs.sendMessage(tab.id, {action: "disableProxy"}, function(response) { * @returns {Promise<object>}
if (chrome.runtime.lastError) { */
console.warn("Warning: Could not send message to tab " + tab.id + ". Tab might have been closed.", chrome.runtime.lastError.message); async function handleMessage(message, sender) {
} switch (message.action) {
}); // ============================================
} // Connection Actions
}); // ============================================
} else if (message.action === "updateProxy") {
console.log("Updating proxy settings..."); case 'connect':
if (message.type === "custom") { return handleConnect(message.mode, message.options);
// Custom proxy enable
applyProxySettings(message.proxy.host, parseInt(message.proxy.port), message.exceptions || []); case 'disconnect':
chrome.storage.local.set({ return handleDisconnect();
proxyEnabled: true,
proxyType: "custom", case 'getStatus':
proxyIP: message.proxy.host, return handleGetStatus();
proxyPort: message.proxy.port
}); // ============================================
sendResponse({ status: "enabled", proxy: message.proxy }); // Proxy List Actions
chrome.tabs.query({}, function(tabs) { // ============================================
for (let tab of tabs) {
chrome.tabs.sendMessage(tab.id, {action: "updatePopupState"}, function(response) { case 'fetchProxies':
if (chrome.runtime.lastError) { return handleFetchProxies(message.source);
console.warn("Warning: Could not send message to tab " + tab.id + ". Tab might have been closed.", chrome.runtime.lastError.message);
} case 'getProxyList':
}); return handleGetProxyList();
}
}); case 'testProxy':
} else if (message.type === "public") { return handleTestProxy(message.proxy);
// Public proxy enable
chrome.runtime.sendMessage({ action: "showLoadingMessage", message: "Please wait..." }); // ============================================
enableFirstWorkingProxy((success) => { // Settings Actions
if (success) { // ============================================
console.log("Public proxy enabled successfully");
sendResponse({ status: "enabled", proxy: proxies[currentProxyIndex] }); case 'getSettings':
} else { return handleGetSettings();
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." }); case 'saveSettings':
} return handleSaveSettings(message.settings);
});
return true; // For async response case 'clearCustomProxy':
} else if (message.type === "disabled") { return handleClearCustomProxy();
clearProxySettings();
chrome.storage.local.set({ proxyEnabled: false, proxyType: null }); case 'setMode':
sendResponse({ status: "disabled" }); return handleSetMode(message.mode);
chrome.tabs.query({}, function(tabs) {
for (let tab of tabs) { case 'nextProxy':
chrome.tabs.sendMessage(tab.id, {action: "disableProxy"}, function(response) { return handleNextProxy();
if (chrome.runtime.lastError) {
console.warn("Warning: Could not send message to tab " + tab.id + ". Tab might have been closed.", chrome.runtime.lastError.message); // ============================================
} // Utility Actions
}); // ============================================
}
}); case 'openOptions':
} chrome.runtime.openOptionsPage();
} else if (message.action === "proxyFailed") { return { success: true };
console.log("Proxy setup failed:", message.error);
clearProxySettings(); case 'openUrl':
chrome.storage.local.set({ proxyEnabled: false, proxyType: null }); chrome.tabs.create({ url: message.url });
chrome.tabs.query({}, function(tabs) { return { success: true };
for (let tab of tabs) {
chrome.tabs.sendMessage(tab.id, {action: "toggleOff"}, function(response) { default:
if (chrome.runtime.lastError) { Utils.log('warn', 'Unknown action', message.action);
console.warn("Warning: Could not send message to tab " + tab.id + ". Tab might have been closed.", chrome.runtime.lastError.message); return { success: false, error: 'Unknown action' };
}
});
}
});
} 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." });
} }
return true; }
});
// ============================================
// 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 {
result = await ProxyManager.connectCustom(
customProxy.ip,
customProxy.port,
customProxy.exceptions,
bypassLocal
);
}
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 {
broadcastMessage({
action: 'statusUpdate',
status: 'error',
error: result.error
});
}
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 {
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"); /* ANyONe Extension v2 - Options Page Controller */
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');
// Validate IP address // ES Module imports
function isValidIP(ip) { import { Utils } from './utils.js';
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); // ============================================
// DOM Elements
// ============================================
const elements = {
// General
autoConnect: document.getElementById('auto-connect'),
defaultMode: document.getElementById('default-mode'),
// Privacy
webrtcProtection: document.getElementById('webrtc-protection'),
killSwitch: document.getElementById('kill-switch'),
bypassLocal: document.getElementById('bypass-local'),
// 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')
};
// ============================================
// 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');
} }
// Validate port number // ============================================
function isValidPort(port) { // Load Settings
const num = parseInt(port, 10); // ============================================
return num > 0 && num <= 65535;
}
// Load saved settings on page load async function loadSettings() {
chrome.storage.local.get(["proxyIP", "proxyPort", "proxyType", "noProxyFor"], (settings) => { const response = await sendMessage({ action: 'getSettings' });
if (chrome.runtime.lastError) {
console.error("Error retrieving settings:", chrome.runtime.lastError); if (!response.success) {
console.log('Failed to load settings');
return; return;
} }
proxyIP.value = settings.proxyIP || "";
proxyPort.value = settings.proxyPort || "";
noProxyFor.value = settings.noProxyFor || "";
});
// Function to check internet connectivity with a timeout const settings = response.settings;
function checkInternetConnection(host, port) {
return new Promise((resolve, reject) => {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout
const proxyConfig = { // General
mode: "fixed_servers", elements.autoConnect.checked = settings.autoConnect || false;
rules: { elements.defaultMode.value = settings.connectionMode || 'public';
singleProxy: {
scheme: "socks5", // Privacy
host: host, elements.webrtcProtection.checked = settings.webrtcProtection !== false;
port: parseInt(port, 10) elements.killSwitch.checked = settings.killSwitch || false;
}, elements.bypassLocal.checked = settings.bypassLocal !== false;
bypassList: [""]
// 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 || '';
}
// ============================================
// 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');
}
}
chrome.proxy.settings.set({ value: proxyConfig, scope: "regular" }, () => { async function saveBypassLocalSetting() {
if (chrome.runtime.lastError) { await sendMessage({
clearTimeout(timeoutId); action: 'saveSettings',
reject(chrome.runtime.lastError.message); settings: {
return; webrtcProtection: elements.webrtcProtection.checked,
} killSwitch: elements.killSwitch.checked,
bypassLocal: elements.bypassLocal.checked
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
}
})
.catch(error => {
clearTimeout(timeoutId);
if (error.name === 'AbortError') {
reject("Connection timed out");
} else {
reject(error.message || "Network error encountered");
}
});
});
}); });
} }
// Function to apply Proxy Settings after saving function showModal(title, message, onConfirm) {
function applyProxySettings(host, port, exceptions = []) { elements.modalTitle.textContent = title;
const proxyConfig = { elements.modalMessage.textContent = message;
mode: "fixed_servers", elements.modalOverlay.classList.add('show');
rules: {
singleProxy: { // Remove old listeners
scheme: "socks5", const newCancelBtn = elements.modalCancel.cloneNode(true);
host: host, const newConfirmBtn = elements.modalConfirm.cloneNode(true);
port: parseInt(port, 10) elements.modalCancel.parentNode.replaceChild(newCancelBtn, elements.modalCancel);
}, elements.modalConfirm.parentNode.replaceChild(newConfirmBtn, elements.modalConfirm);
bypassList: exceptions.concat([""]) 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 = {
chrome.proxy.settings.set({ value: proxyConfig, scope: "regular" }, () => { git: 'git.debros.io/DeBros/anyone-proxy-list',
if (chrome.runtime.lastError) { github: 'github.com/DeBrosOfficial/anyone-proxy-list',
statusMessage.textContent = "Error applying proxy: " + chrome.runtime.lastError.message; arweave: 'arweave.net/FjxfWIbS...B8'
statusMessage.style.color = "red"; };
} else { const fullUrls = {
console.log(`Proxy applied: ${host}:${port}`); git: 'https://git.debros.io/DeBros/anyone-proxy-list',
statusMessage.textContent = `Proxy applied: ${host}:${port}`; github: 'https://github.com/DeBrosOfficial/anyone-proxy-list',
statusMessage.style.color = "#2ecc71"; arweave: 'https://arweave.net/FjxfWIbSnZb7EaJWbeuWCsBBFWjTppfS3_KHxUP__B8'
clearStatusMessage(); };
} 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;
} }
// Function to clear status message after a delay async function saveCustomProxy() {
function clearStatusMessage() { const ip = elements.customIp.value.trim();
setTimeout(() => { const port = elements.customPort.value.trim();
statusMessage.textContent = ""; const username = elements.customUsername.value.trim();
}, 5000); // Clear message after 5 seconds (5000 milliseconds) const password = elements.customPassword.value;
}
let isCheckingProxy = false; // Validate
if (!ip) {
// Save settings when clicking "Save Settings" showToast('Please enter host address', 'error');
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;
}
if (!isValidPort(proxyPort.value)) {
statusMessage.textContent = "Invalid port number. Must be between 1 and 65535.";
statusMessage.style.color = "red";
return; return;
} }
statusMessage.textContent = "Please wait..."; if (!Utils.isValidHost(ip)) {
statusMessage.style.color = "#f39c12"; showToast('Invalid IP address or hostname', 'error');
return;
}
if (!port || !Utils.isValidPort(port)) {
showToast('Invalid port number (1-65535)', 'error');
return;
}
// Disable the saveSettings button // Disable button during save
saveSettings.disabled = true; elements.btnSaveCustom.disabled = true;
isCheckingProxy = true; const originalText = elements.btnSaveCustom.innerHTML;
elements.btnSaveCustom.innerHTML = '<span>Saving...</span>';
const noProxyExceptions = noProxyFor.value.split(',').map(ex => ex.trim()); try {
const filteredExceptions = noProxyExceptions.filter(ex => ex !== ''); // Save settings (bypass list is saved separately in Privacy section)
await sendMessage({
checkInternetConnection(proxyIP.value, proxyPort.value) action: 'saveSettings',
.then(() => { settings: {
chrome.storage.local.set({ proxyIP: ip,
proxyIP: proxyIP.value, proxyPort: parseInt(port, 10),
proxyPort: proxyPort.value, proxyUsername: username,
proxyType: "custom", proxyPassword: password
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", () => { showToast('Custom proxy settings saved', 'success');
chrome.proxy.settings.clear({}, () => {
if (chrome.runtime.lastError) { } finally {
statusMessage.textContent = "Error disabling proxy: " + chrome.runtime.lastError.message; elements.btnSaveCustom.disabled = false;
statusMessage.style.color = "red"; 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 { } else {
statusMessage.textContent = "Proxy has been disabled!"; showToast('Failed to clear settings', 'error');
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();
} }
}); } catch (error) {
}); showToast('Failed to clear settings', 'error');
} finally {
elements.btnClearCustom.disabled = false;
elements.btnClearCustom.innerHTML = originalText;
}
}
// Open Check Anyone page // ============================================
checkAnyoneButton.addEventListener("click", () => { // Proxy Actions
window.open("https://check.en.anyone.tech/", "_blank"); // ============================================
});
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
}
});
}
// 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(() => {
elements.toast.classList.remove('show');
}, 3000);
}
// 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');
}
}
});

View File

@ -1,188 +1,695 @@
document.addEventListener("DOMContentLoaded", () => { /* ANyONe Extension v2 - Popup Controller */
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");
if (!checkAnyoneButton) { // ES Module imports
checkAnyoneButton = document.createElement("button"); import { CONFIG } from './config.js';
checkAnyoneButton.id = "checkAnyoneButton"; import { Utils } from './utils.js';
checkAnyoneButton.textContent = "Check ANyONe";
checkAnyoneButton.style.marginBottom = "10px"; // ============================================
checkAnyoneButton.addEventListener("click", () => { // State
window.open("https://check.en.anyone.tech/", "_blank"); // ============================================
});
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);
}
// ============================================
// 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);
}
// ============================================
// 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('[Popup] Failed to load proxy info:', response.error);
elements.proxyCount.textContent = 'Error loading';
showNoProxiesWarning();
}
}
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;
} }
if (!dappStoreButton) { // Show switching state (orange)
dappStoreButton = document.createElement("button"); state.connecting = true;
dappStoreButton.id = "dappStoreButton"; updateConnectionUI();
dappStoreButton.textContent = "dApp Store"; elements.statusText.textContent = 'Switching...';
dappStoreButton.style.marginBottom = "10px"; elements.statusText.style.color = 'var(--color-warning)';
dappStoreButton.addEventListener("click", () => { elements.statusIp.style.display = 'none';
const storeUrl = chrome.runtime.getURL("store.html");
chrome.tabs.create({ url: storeUrl }); 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;
} }
if (updateProxiesButton) { const options = {};
updateProxiesButton.addEventListener("click", () => { let customIp = null;
console.log("Update Proxies button clicked in popup.js"); let customPort = null;
statusMessage.textContent = "Updating proxies...";
statusMessage.style.color = "#f39c12"; // Orange for loading if (state.mode === 'public') {
chrome.runtime.sendMessage({ action: "updateProxies" }, (response) => { options.country = 'auto';
if (response && response.success) {
console.log('Proxy list update was successful');
statusMessage.textContent = 'Proxies updated successfully!';
statusMessage.style.color = "#2ecc71"; // Green for success
} else {
console.log('Proxy list update failed');
statusMessage.textContent = 'Failed to update proxies.';
statusMessage.style.color = "#e74c3c"; // Red for failure
}
// Clear the update message after 3 seconds and reinitialize the UI
setTimeout(() => {
initializeUI();
}, 3000);
});
});
} }
const buttonContainer = document.querySelector(".button-container"); // Save custom proxy before connecting if in custom mode
if (buttonContainer) { if (state.mode === 'custom') {
buttonContainer.innerHTML = ''; customIp = elements.customIp.value.trim();
buttonContainer.appendChild(optionsButton); customPort = elements.customPort.value.trim();
buttonContainer.appendChild(dappStoreButton);
buttonContainer.appendChild(updateProxiesButton);
}
if (statusMessage && !checkAnyoneButton.parentNode) { if (!customIp || !customPort) {
statusMessage.parentNode.insertBefore(checkAnyoneButton, statusMessage.nextSibling); state.connecting = false;
} showError('Please enter host and port');
return;
}
function updateStatusMessage(isEnabled, proxyType, proxy) { // Validate host and port
if (isEnabled) { if (!Utils.isValidHost(customIp)) {
if (proxyType === "custom") { state.connecting = false;
statusMessage.textContent = `Custom Proxy is ENABLED and routing through ${proxy.host}:${proxy.port}`; showError('Invalid IP address or hostname');
statusMessage.style.color = "#2ecc71"; // Green for custom return;
} else { }
statusMessage.textContent = `Public Proxy is ENABLED and routing through ${proxy.host}:${proxy.port}`;
statusMessage.style.color = "#03bdc5"; // Blue for public 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)
} }
} 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
}, 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
updateUI(isEnabled, proxyType, currentProxy);
}); });
} }
initializeUI(); const response = await sendMessage({
action: 'connect',
mode: state.mode,
options
});
proxyToggle.addEventListener("change", () => { state.connecting = false;
const isEnabled = proxyToggle.checked; elements.statusText.style.color = '';
if (isEnabled) { if (response.success) {
chrome.storage.local.get(["proxyType"], (data) => { state.connected = true;
if (data.proxyType === "custom") { state.currentProxy = response.proxy;
chrome.storage.local.get(["proxyIP", "proxyPort"], (settings) => { } else {
chrome.runtime.sendMessage({ action: "updateProxy", type: "custom", proxy: { host: settings.proxyIP, port: settings.proxyPort } }, (response) => { if (state.mode === 'custom') {
if (response && response.status === "enabled") { state.connected = false;
updateUI(true, "custom", { host: settings.proxyIP, port: settings.proxyPort }); state.currentProxy = null;
} else { showErrorPersistent(`Proxy ${customIp}:${customPort} is not responding`);
alert("Failed to enable custom proxy. Please try again."); return;
proxyToggle.checked = false; }
initializeUI(); showError(response.error || 'Connection failed');
} }
});
}); updateConnectionUI();
} else { }
chrome.runtime.sendMessage({ action: "enableProxy" }, (response) => {
if (response && response.status === "enabled" && response.proxy) { async function disconnect() {
chrome.storage.local.set({ proxyEnabled: true, currentProxy: response.proxy, proxyType: "public" }); state.connecting = true;
updateUI(true, "public", response.proxy); updateConnectionUI();
} else {
alert(response.message || "Failed to enable public proxy. Please try again."); const response = await sendMessage({ action: 'disconnect' });
proxyToggle.checked = false;
initializeUI(); 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 { } else {
chrome.runtime.sendMessage({ action: "disableProxy" }, (response) => { showSuccess(`Updated: ${response.proxies.length} proxies`);
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();
}
});
} }
}); await loadProxyInfo();
} else {
showError('All sources failed');
}
}
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { function sendMessage(message) {
if (message.action === "updatePopupState" || message.action === "disableProxy") { return new Promise((resolve) => {
initializeUI(); chrome.runtime.sendMessage(message, (response) => {
} else if (message.action === "showLoadingMessage") { resolve(response || { success: false, error: 'No response' });
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
setTimeout(() => {
statusMessage.textContent = "";
}, 5000); // Clear after 5 seconds
}
}); });
}
// Open options page function handleBackgroundMessage(message) {
optionsButton.addEventListener("click", () => { console.log('[Popup] Background message:', message);
chrome.runtime.openOptionsPage();
});
// Open Check Anyone page switch (message.action) {
checkAnyoneButton.addEventListener("click", () => { case 'statusUpdate':
window.open("https://check.en.anyone.tech/", "_blank"); 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;
// Open dApp Store page case 'proxyError':
dappStoreButton.addEventListener("click", () => { showError(`Proxy error: ${message.error}`);
const storeUrl = chrome.runtime.getURL("html/store.html"); break;
chrome.tabs.create({ url: storeUrl });
}); 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(() => {
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);
}
function showErrorPersistent(message) {
console.log('[Popup] Error:', message);
// 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 button
elements.btnConnect.classList.remove('connected', 'connecting');
elements.btnConnect.classList.add('error');
elements.btnConnect.disabled = false;
// Return to disconnected state after 5 seconds
setTimeout(() => {
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);
}
function showSuccess(message) {
console.log('[Popup] Success:', message);
// "Success" in green
elements.statusText.textContent = 'Success';
elements.statusText.style.color = 'var(--color-success)';
// 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, "manifest_version": 3,
"name": "ANyONe Extension", "name": "ANyONe Extension",
"version": "1.0.5", "version": "2.0.0",
"description": "Manage Socks5 proxy settings", "description": "Privacy-focused Socks5 proxy management",
"permissions": [ "permissions": [
"proxy", "proxy",
"storage", "storage",
"tabs", "tabs",
"scripting" "scripting",
"privacy",
"webRequest",
"webRequestAuthProvider"
], ],
"host_permissions": ["<all_urls>"], "host_permissions": ["<all_urls>"],
"background": { "background": {
"service_worker": "js/background.js" "service_worker": "js/background.js",
"type": "module"
}, },
"action": { "action": {
"default_popup": "html/popup.html", "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": { "options_ui": {
"page": "html/options.html", "page": "html/options.html",