Compare commits

..

No commits in common. "1e4033e1f18b7541269d0514d3cb7425c9f1e98a" and "99124621697f6d92b46e24e28246630c24a8c482" have entirely different histories.

25 changed files with 1418 additions and 5173 deletions

262
README.md
View File

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

View File

@ -1,395 +0,0 @@
/* 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); }

View File

@ -1,636 +0,0 @@
/* 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;
}

View File

@ -1,493 +0,0 @@
/* 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;
}

View File

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

View File

@ -1,121 +1,225 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html>
<head> <head>
<meta charset="UTF-8"> <title>ANyONe Network Gateway</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <style>
<title>ANyONe Extension</title> body {
<link rel="stylesheet" href="../css/popup.css"> font-family: Arial, Helvetica, sans-serif;
</head> margin: 0;
<body> padding: 10px 30px 10px;
<div class="popup-container"> background-image: url('../images/popupback.png');
<!-- Header --> background-size: cover;
<header class="popup-header"> background-repeat: no-repeat;
<div class="logo"> background-position: center;
<img src="../images/anyone2.png" alt="ANyONe" class="logo-img"> background-color: #6d8392;
<span class="logo-subtitle">Privacy Extension</span> color: white;
</div> text-align: center;
</header> width: 220px;
height: 500px;
overflow: hidden;
}
<!-- Mode Selection --> #anonLogo {
<section class="mode-section"> margin: 10px auto 10px;
<div class="mode-tabs"> display: block;
<button class="mode-tab active" data-mode="public">Public Proxies</button> }
<button class="mode-tab" data-mode="custom">Custom Proxy</button>
</div>
</section>
<!-- Mode Content: Public --> .switch {
<section class="mode-content active" id="mode-public"> position: relative;
<div class="card"> display: inline-block;
<div class="proxy-info"> width: 50px;
<div class="proxy-info-row"> height: 15px;
<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-connect-text">Connect</button>
<button class="btn btn-secondary" id="btn-next-proxy">Next Proxy</button>
</div>
</div>
</section>
<!-- Mode Content: Custom --> .switch input {
<section class="mode-content" id="mode-custom"> opacity: 1;
<div class="card"> width: 0;
<div class="custom-form"> height: 0;
<div class="input-row"> }
<div class="input-group">
<label class="input-label">IP / Hostname</label>
<input type="text" class="input" id="custom-ip" placeholder="relayup.local">
</div>
<div class="input-group port">
<label class="input-label">Port</label>
<input type="number" class="input" id="custom-port" placeholder="9050">
</div>
</div>
<button class="btn btn-secondary w-full" id="btn-test-custom">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
<polyline points="22 4 12 14.01 9 11.01"/>
</svg>
Test Connection
</button>
</div>
</div>
</section>
<!-- Connect Button --> .slider {
<section class="connect-section"> position: absolute;
<button class="connect-btn" id="btn-connect"> cursor: pointer;
<div class="connect-btn-ring"></div> top: 0;
<img src="../images/anonlogo.png" alt="Connect" class="connect-btn-icon"> left: 0;
</button> right: 0;
</section> bottom: 0;
background-color: #e74c3c;
transition: 0.4s;
border-radius: 34px;
}
<!-- Status Card --> .slider:before {
<section class="status-card" id="status-card"> position: absolute;
<div class="status-main"> content: "";
<div class="status-dot-container"> height: 25px;
<span class="status-dot offline" id="status-dot"></span> width: 25px;
</div> left: 0px;
<div class="status-info"> bottom: -4px;
<span class="status-text" id="status-text">Disconnected</span> background-color: #ffffff;
<span class="status-ip" id="status-ip" style="display: none;">-</span> transition: 0.4s;
</div> border-radius: 50%;
</div> }
</section>
<!-- Quick Actions --> input:checked + .slider {
<section class="quick-actions"> background-color: #02af50;
<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"/> input:checked + .slider:before {
<line x1="21" y1="21" x2="16.65" y2="16.65"/> transform: translateX(26px);
</svg> }
<span class="quick-action-label">Check IP</span>
</button> #statusMessage {
<button class="quick-action" id="btn-refresh"> margin-top: 30px;
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> font-size: 16px;
<polyline points="23 4 23 10 17 10"/> font-weight: bold;
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/> min-height: 30px;
</svg> max-width: 100%;
<span class="quick-action-label">Refresh</span> overflow-wrap: break-word;
</button> }
<button class="quick-action" id="btn-settings">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> .button-container-1 {
<circle cx="12" cy="12" r="3"/> margin-top: 10px;
<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"/> margin-bottom: 5px;
</svg> display: flex;
<span class="quick-action-label">Settings</span> justify-content: center;
</button> flex-wrap: nowrap;
</section> 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>
<script type="module" src="../js/popup.js"></script> <div class="button-container-2">
</body> <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> </html>

46
html/store.html Normal file
View File

@ -0,0 +1,46 @@
<!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.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

BIN
images/comingsoon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 975 KiB

BIN
images/gitbros.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
images/optionsback.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

BIN
images/popupback.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
images/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
images/x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -1,728 +1,286 @@
/* ANyONe Extension v2 - Background Service Worker */ let proxies = []; // Initially empty, will be populated from local storage or fetched from the server
let currentProxyIndex = 0; // Start with the first proxy
let proxyEnabled = false;
// ES Module imports console.log("background.js is running");
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 = []) {
// Initialization const proxyConfig = {
// ============================================ mode: "fixed_servers",
Utils.log('info', 'Background service worker starting...');
// Initialize on startup
chrome.runtime.onInstalled.addListener(async (details) => {
Utils.log('info', `Extension ${details.reason}`, { version: CONFIG.VERSION });
if (details.reason === 'install') {
// First install
Utils.log('info', 'First install, setting up defaults...');
// Set defaults
await Storage.set({
[CONFIG.STORAGE_KEYS.MODE]: CONFIG.DEFAULTS.MODE,
[CONFIG.STORAGE_KEYS.PROXY_ENABLED]: false,
[CONFIG.STORAGE_KEYS.AUTO_CONNECT]: CONFIG.DEFAULTS.AUTO_CONNECT,
[CONFIG.STORAGE_KEYS.WEBRTC_PROTECTION]: CONFIG.DEFAULTS.WEBRTC_PROTECTION,
[CONFIG.STORAGE_KEYS.BYPASS_LOCAL]: CONFIG.DEFAULTS.BYPASS_LOCAL
});
// Auto-fetch proxies on first install
Utils.log('info', 'Fetching initial proxy list...');
try {
const source = CONFIG.DEFAULTS.PROXY_SOURCE;
await handleFetchProxies(source);
Utils.log('info', 'Initial proxy list fetched successfully');
} catch (error) {
Utils.log('error', 'Failed to fetch initial proxy list', error);
}
} else if (details.reason === 'update') {
Utils.log('info', 'Extension updated');
}
});
// Load state on startup
chrome.runtime.onStartup.addListener(async () => {
Utils.log('info', 'Browser startup');
await ProxyManager.init();
// Check auto-connect setting
const autoConnect = await Storage.getValue(CONFIG.STORAGE_KEYS.AUTO_CONNECT, false);
if (autoConnect) {
Utils.log('info', 'Auto-connect enabled, connecting...');
const mode = await Storage.getMode();
await handleConnect(mode);
}
});
// ============================================
// Message Handlers
// ============================================
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
Utils.log('debug', 'Message received', message);
// Handle async responses
handleMessage(message, sender)
.then(response => sendResponse(response))
.catch(error => {
Utils.log('error', 'Message handler error', error);
sendResponse({ success: false, error: error.message });
});
return true; // Keep channel open for async response
});
/**
* Handle incoming messages
* @param {object} message - Message object
* @param {object} sender - Sender info
* @returns {Promise<object>}
*/
async function handleMessage(message, sender) {
switch (message.action) {
// ============================================
// Connection Actions
// ============================================
case 'connect':
return handleConnect(message.mode, message.options);
case 'disconnect':
return handleDisconnect();
case 'getStatus':
return handleGetStatus();
// ============================================
// Proxy List Actions
// ============================================
case 'fetchProxies':
return handleFetchProxies(message.source);
case 'getProxyList':
return handleGetProxyList();
case 'testProxy':
return handleTestProxy(message.proxy);
// ============================================
// Settings Actions
// ============================================
case 'getSettings':
return handleGetSettings();
case 'saveSettings':
return handleSaveSettings(message.settings);
case 'clearCustomProxy':
return handleClearCustomProxy();
case 'setMode':
return handleSetMode(message.mode);
case 'nextProxy':
return handleNextProxy();
// ============================================
// Utility Actions
// ============================================
case 'openOptions':
chrome.runtime.openOptionsPage();
return { success: true };
case 'openUrl':
chrome.tabs.create({ url: message.url });
return { success: true };
default:
Utils.log('warn', 'Unknown action', message.action);
return { success: false, error: 'Unknown action' };
}
}
// ============================================
// Action Handlers
// ============================================
/**
* Handle connect action
* @param {string} mode - Connection mode
* @param {object} options - Additional options
* @returns {Promise<object>}
*/
async function handleConnect(mode, options = {}) {
Utils.log('info', 'Connecting', { mode, options });
// Deactivate kill switch if it was active
const killSwitchActive = await Storage.getValue('killSwitchActive', false);
if (killSwitchActive) {
await applyKillSwitch(false);
Utils.log('info', 'Kill switch deactivated for new connection');
}
// Notify popup of loading state
broadcastMessage({ action: 'statusUpdate', status: 'connecting' });
// Get local network access setting and exceptions (bypass list)
const bypassLocal = await Storage.getValue(CONFIG.STORAGE_KEYS.BYPASS_LOCAL, true);
const exceptions = await Storage.getValue(CONFIG.STORAGE_KEYS.EXCEPTIONS, []);
let result;
switch (mode) {
case CONFIG.MODES.PUBLIC:
await ProxyManager.init();
result = await ProxyManager.connectToFastest(options.country, bypassLocal, exceptions);
break;
case CONFIG.MODES.CUSTOM:
const customProxy = await Storage.getCustomProxy();
Utils.log('info', 'Custom proxy settings loaded', {
ip: customProxy.ip,
port: customProxy.port,
exceptions: customProxy.exceptions,
bypassLocal
});
if (!customProxy.ip || !customProxy.port) {
result = { success: false, error: 'Custom proxy not configured' };
} else {
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: { rules: {
singleProxy: { singleProxy: {
scheme: 'socks5', scheme: "socks5",
host: '127.0.0.1', // Localhost with closed port - blocks all traffic host,
port: 65535 port,
},
bypassList: exceptions.concat([""]),
},
};
chrome.proxy.settings.set({ value: proxyConfig, scope: "regular" }, () => {
const lastError = chrome.runtime.lastError;
if (lastError) {
console.error("Error applying proxy settings:", lastError);
fallbackToNextProxy(); // Move to the next proxy if the current one fails
} else {
console.log(`Proxy applied: ${host}:${port}`);
proxyEnabled = true;
} }
});
}
function clearProxySettings() {
chrome.proxy.settings.clear({}, () => {
const lastError = chrome.runtime.lastError;
if (lastError) {
console.error("Error clearing proxy settings:", lastError);
} else {
console.log("Proxy settings cleared.");
proxyEnabled = false;
currentProxyIndex = 0; // Reset to the first proxy
}
});
}
function fallbackToNextProxy() {
currentProxyIndex = (currentProxyIndex + 1) % proxies.length; // Move to the next proxy in the list
if (proxies.length > 0) {
const { host, port } = proxies[currentProxyIndex];
console.log(`Falling back to proxy: ${host}:${port}`);
applyProxySettings(host, port);
} else {
console.error("No proxies available to fall back to.");
}
}
function checkProxyFunctionality(proxy, callback) {
const proxyConfig = {
mode: "fixed_servers",
rules: {
singleProxy: {
scheme: "socks5",
host: proxy.host,
port: proxy.port
},
bypassList: [""]
} }
}; };
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 chrome.proxy.settings.set({ value: proxyConfig, scope: "regular" }, () => {
try { if (chrome.runtime.lastError) {
await chrome.action.setBadgeText({ text: '!' }); console.error("Error setting proxy for check:", chrome.runtime.lastError);
await chrome.action.setBadgeBackgroundColor({ color: '#e74c3c' }); callback(false);
} 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; return;
} }
try { const controller = new AbortController();
// Get stored credentials const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout
const customProxy = await Storage.getCustomProxy();
if (customProxy.username && customProxy.password) { fetch('https://check.en.anyone.tech', {
Utils.log('info', 'Providing proxy credentials'); mode: 'no-cors',
callback({ signal: controller.signal
authCredentials: { })
username: customProxy.username, .then(response => {
password: customProxy.password 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();
} }
}); });
} 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'); tryNextProxy();
}
function fetchProxies() {
console.log("Starting to fetch proxies...");
return fetch('https://git.debros.io/DeBros/anyone-proxy-list/raw/branch/main/anonproxies.json') // list of public proxies
.then(response => {
console.log("Response status:", response.status);
if (!response.ok) {
console.error("Response not OK");
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
console.log("Data received:", data);
proxies = data;
// Save the proxies to local storage
chrome.storage.local.set({ 'proxyList': proxies }, () => {
console.log('Proxies updated and saved:', proxies);
});
})
.catch(error => {
console.error('Failed to fetch proxies:', error);
});
}
// Load proxies from storage on extension startup if they exist
chrome.storage.local.get('proxyList', function(result) {
if (result.proxyList) {
proxies = result.proxyList;
console.log('Loaded proxies from storage:', proxies);
} else {
console.log('No proxies found in local storage, fetch required.');
}
});
// This event listener runs when the extension is first installed or updated
chrome.runtime.onInstalled.addListener((details) => {
if (details.reason === "install") {
console.log("Extension installed for the first time. Fetching proxies...");
// Fetch proxies automatically on first install but don't enable them
fetchProxies().then(() => {
console.log("Proxies updated on first install.");
// Don't enable proxy automatically, just fetch and store it
});
}
});
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
console.log("Received message in background:", message);
if (message.action === "enableProxy") {
console.log("Enabling proxy...");
chrome.runtime.sendMessage({ action: "showLoadingMessage", message: "Please wait..." });
enableFirstWorkingProxy((success) => {
if (success) {
console.log("Proxy enabled successfully");
sendResponse({ status: "enabled", proxy: proxies[currentProxyIndex] });
} 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") {
console.log("Disabling proxy...");
clearProxySettings();
chrome.storage.local.set({ proxyEnabled: false, proxyType: null });
sendResponse({ status: "disabled" });
chrome.tabs.query({}, function(tabs) {
for (let tab of tabs) {
chrome.tabs.sendMessage(tab.id, {action: "disableProxy"}, function(response) {
if (chrome.runtime.lastError) {
console.warn("Warning: Could not send message to tab " + tab.id + ". Tab might have been closed.", chrome.runtime.lastError.message);
}
});
}
});
} else if (message.action === "updateProxy") {
console.log("Updating proxy settings...");
if (message.type === "custom") {
// Custom proxy enable
applyProxySettings(message.proxy.host, parseInt(message.proxy.port), message.exceptions || []);
chrome.storage.local.set({
proxyEnabled: true,
proxyType: "custom",
proxyIP: message.proxy.host,
proxyPort: message.proxy.port
});
sendResponse({ status: "enabled", proxy: message.proxy });
chrome.tabs.query({}, function(tabs) {
for (let tab of tabs) {
chrome.tabs.sendMessage(tab.id, {action: "updatePopupState"}, function(response) {
if (chrome.runtime.lastError) {
console.warn("Warning: Could not send message to tab " + tab.id + ". Tab might have been closed.", chrome.runtime.lastError.message);
}
});
}
});
} else if (message.type === "public") {
// Public proxy enable
chrome.runtime.sendMessage({ action: "showLoadingMessage", message: "Please wait..." });
enableFirstWorkingProxy((success) => {
if (success) {
console.log("Public proxy enabled successfully");
sendResponse({ status: "enabled", proxy: proxies[currentProxyIndex] });
} else {
console.log("Failed to enable public proxy");
sendResponse({ status: "error", message: "No public proxy available at this moment. Please configure a custom proxy in the settings." });
}
});
return true; // For async response
} else if (message.type === "disabled") {
clearProxySettings();
chrome.storage.local.set({ proxyEnabled: false, proxyType: null });
sendResponse({ status: "disabled" });
chrome.tabs.query({}, function(tabs) {
for (let tab of tabs) {
chrome.tabs.sendMessage(tab.id, {action: "disableProxy"}, function(response) {
if (chrome.runtime.lastError) {
console.warn("Warning: Could not send message to tab " + tab.id + ". Tab might have been closed.", chrome.runtime.lastError.message);
}
});
}
});
}
} else if (message.action === "proxyFailed") {
console.log("Proxy setup failed:", message.error);
clearProxySettings();
chrome.storage.local.set({ proxyEnabled: false, proxyType: null });
chrome.tabs.query({}, function(tabs) {
for (let tab of tabs) {
chrome.tabs.sendMessage(tab.id, {action: "toggleOff"}, function(response) {
if (chrome.runtime.lastError) {
console.warn("Warning: Could not send message to tab " + tab.id + ". Tab might have been closed.", chrome.runtime.lastError.message);
}
});
}
});
} 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;
});

View File

@ -1,87 +0,0 @@
/* ANyONe Extension v2 - Configuration */
const CONFIG = {
// Version
VERSION: '2.0.1',
// 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,558 +1,226 @@
/* ANyONe Extension v2 - Options Page Controller */ const proxyIP = document.getElementById("proxyIP");
const proxyPort = document.getElementById("proxyPort");
const noProxyFor = document.getElementById("noProxyFor");
const saveSettings = document.getElementById("saveSettings");
const disableProxy = document.getElementById("disableProxy");
const statusMessage = document.getElementById("statusMessage");
const checkAnyoneButton = document.getElementById('checkAnyoneButton');
// ES Module imports // Validate IP address
import { Utils } from './utils.js'; function isValidIP(ip) {
const ipRegex = /^(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})\.(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})\.(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})\.(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})$/;
// ============================================ return ipRegex.test(ip);
// 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
// Load Settings function isValidPort(port) {
// ============================================ const num = parseInt(port, 10);
return num > 0 && num <= 65535;
}
async function loadSettings() { // Load saved settings on page load
const response = await sendMessage({ action: 'getSettings' }); chrome.storage.local.get(["proxyIP", "proxyPort", "proxyType", "noProxyFor"], (settings) => {
if (chrome.runtime.lastError) {
if (!response.success) { console.error("Error retrieving settings:", chrome.runtime.lastError);
console.log('Failed to load settings');
return; return;
} }
proxyIP.value = settings.proxyIP || "";
const settings = response.settings; proxyPort.value = settings.proxyPort || "";
noProxyFor.value = settings.noProxyFor || "";
// General });
elements.autoConnect.checked = settings.autoConnect || false;
elements.defaultMode.value = settings.connectionMode || 'public'; // Function to check internet connectivity with a timeout
function checkInternetConnection(host, port) {
// Privacy return new Promise((resolve, reject) => {
elements.webrtcProtection.checked = settings.webrtcProtection !== false; const controller = new AbortController();
elements.killSwitch.checked = settings.killSwitch || false; const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout
elements.bypassLocal.checked = settings.bypassLocal !== false;
const proxyConfig = {
// Public Proxies mode: "fixed_servers",
elements.proxySource.value = settings.proxySource || 'git'; rules: {
elements.updateInterval.value = settings.updateInterval ?? 0; singleProxy: {
updateSourceNameDisplay(settings.proxySource || 'git'); scheme: "socks5",
host: host,
// Custom Proxy port: parseInt(port, 10)
elements.customIp.value = settings.proxyIP || ''; },
elements.customPort.value = settings.proxyPort || ''; bypassList: [""]
elements.customUsername.value = settings.proxyUsername || ''; }
elements.customPassword.value = settings.proxyPassword || ''; };
elements.exceptions.value = Array.isArray(settings.noProxyFor)
? settings.noProxyFor.join(', ') chrome.proxy.settings.set({ value: proxyConfig, scope: "regular" }, () => {
: settings.noProxyFor || ''; if (chrome.runtime.lastError) {
} clearTimeout(timeoutId);
reject(chrome.runtime.lastError.message);
// ============================================ return;
// Event Listeners }
// ============================================
fetch('https://check.en.anyone.tech', {
function setupEventListeners() { method: 'GET',
// Auto-save on toggle changes mode: 'no-cors',
elements.autoConnect.addEventListener('change', saveGeneralSettings); signal: controller.signal
elements.defaultMode.addEventListener('change', saveGeneralSettings); })
elements.webrtcProtection.addEventListener('change', savePrivacySettings); .then(response => {
elements.killSwitch.addEventListener('change', savePrivacySettings); clearTimeout(timeoutId);
elements.bypassLocal.addEventListener('change', handleBypassLocalChange); if (response.ok) {
elements.proxySource.addEventListener('change', handleProxySourceChange); resolve(true); // Connection successful
elements.updateInterval.addEventListener('change', saveProxySourceSettings); } else {
reject("Failed to connect via proxy"); // Connection failed
// Bypass list buttons }
elements.btnClearBypass.addEventListener('click', clearBypassList); })
elements.btnSaveBypass.addEventListener('click', saveBypassList); .catch(error => {
clearTimeout(timeoutId);
// Buttons if (error.name === 'AbortError') {
elements.btnRefreshProxies.addEventListener('click', refreshProxies); reject("Connection timed out");
elements.btnClearCustom.addEventListener('click', clearCustomProxy); } else {
elements.btnTestCustom.addEventListener('click', testCustomProxy); reject(error.message || "Network error encountered");
elements.btnSaveCustom.addEventListener('click', saveCustomProxy); }
} });
});
// ============================================ });
// Save Settings }
// ============================================
// Function to apply Proxy Settings after saving
async function saveGeneralSettings() { function applyProxySettings(host, port, exceptions = []) {
await sendMessage({ const proxyConfig = {
action: 'saveSettings', mode: "fixed_servers",
settings: { rules: {
autoConnect: elements.autoConnect.checked, singleProxy: {
connectionMode: elements.defaultMode.value scheme: "socks5",
} host: host,
}); port: parseInt(port, 10)
showToast('General settings saved'); },
} bypassList: exceptions.concat([""])
}
async function savePrivacySettings() { };
await sendMessage({
action: 'saveSettings', chrome.proxy.settings.set({ value: proxyConfig, scope: "regular" }, () => {
settings: { if (chrome.runtime.lastError) {
webrtcProtection: elements.webrtcProtection.checked, statusMessage.textContent = "Error applying proxy: " + chrome.runtime.lastError.message;
killSwitch: elements.killSwitch.checked, statusMessage.style.color = "red";
bypassLocal: elements.bypassLocal.checked } else {
} console.log(`Proxy applied: ${host}:${port}`);
}); statusMessage.textContent = `Proxy applied: ${host}:${port}`;
showToast('Privacy settings saved'); statusMessage.style.color = "#2ecc71";
} clearStatusMessage();
}
async function handleBypassLocalChange() { });
if (!elements.bypassLocal.checked) { }
// Revert toggle immediately, wait for confirmation
elements.bypassLocal.checked = true; // Function to clear status message after a delay
function clearStatusMessage() {
// Show confirmation modal setTimeout(() => {
showModal( statusMessage.textContent = "";
'Disable Local Network Access?', }, 5000); // Clear message after 5 seconds (5000 milliseconds)
'This will make local devices (printers, NAS, router, etc) unreachable while connected to the proxy.', }
async () => {
// User confirmed - disable local network access let isCheckingProxy = false;
elements.bypassLocal.checked = false;
await saveBypassLocalSetting(); // Save settings when clicking "Save Settings"
showToast('Local network access disabled', 'error'); saveSettings.addEventListener("click", () => {
} if (isCheckingProxy) return; // If a check is already in progress, do nothing
);
} else { if (!isValidIP(proxyIP.value)) {
// Enabling - no confirmation needed statusMessage.textContent = "Invalid IP address.";
await saveBypassLocalSetting(); statusMessage.style.color = "red";
showToast('Local network access enabled', 'success'); return;
} }
} if (!isValidPort(proxyPort.value)) {
statusMessage.textContent = "Invalid port number. Must be between 1 and 65535.";
async function saveBypassLocalSetting() { statusMessage.style.color = "red";
await sendMessage({ return;
action: 'saveSettings', }
settings: {
webrtcProtection: elements.webrtcProtection.checked, statusMessage.textContent = "Please wait...";
killSwitch: elements.killSwitch.checked, statusMessage.style.color = "#f39c12";
bypassLocal: elements.bypassLocal.checked
}
}); // Disable the saveSettings button
} saveSettings.disabled = true;
isCheckingProxy = true;
function showModal(title, message, onConfirm) {
elements.modalTitle.textContent = title; const noProxyExceptions = noProxyFor.value.split(',').map(ex => ex.trim());
elements.modalMessage.textContent = message; const filteredExceptions = noProxyExceptions.filter(ex => ex !== '');
elements.modalOverlay.classList.add('show');
checkInternetConnection(proxyIP.value, proxyPort.value)
// Remove old listeners .then(() => {
const newCancelBtn = elements.modalCancel.cloneNode(true); chrome.storage.local.set({
const newConfirmBtn = elements.modalConfirm.cloneNode(true); proxyIP: proxyIP.value,
elements.modalCancel.parentNode.replaceChild(newCancelBtn, elements.modalCancel); proxyPort: proxyPort.value,
elements.modalConfirm.parentNode.replaceChild(newConfirmBtn, elements.modalConfirm); proxyType: "custom",
elements.modalCancel = newCancelBtn; noProxyFor: filteredExceptions.join(", "),
elements.modalConfirm = newConfirmBtn; proxyEnabled: true
}, () => {
// Add new listeners if (chrome.runtime.lastError) {
elements.modalCancel.addEventListener('click', () => { statusMessage.textContent = "Error saving settings: " + chrome.runtime.lastError.message;
elements.modalOverlay.classList.remove('show'); statusMessage.style.color = "red";
}); } else {
statusMessage.textContent = "Proxy settings saved and connection verified!";
elements.modalConfirm.addEventListener('click', () => { statusMessage.style.color = "#2ecc71";
elements.modalOverlay.classList.remove('show'); applyProxySettings(proxyIP.value, proxyPort.value, filteredExceptions);
if (onConfirm) onConfirm();
}); // Send response to the message sender
chrome.runtime.sendMessage({ action: "updateProxy", type: "custom", proxy: { host: proxyIP.value, port: parseInt(proxyPort.value) }, exceptions: filteredExceptions }, (response) => {
// Close on overlay click if (chrome.runtime.lastError) {
elements.modalOverlay.addEventListener('click', (e) => { console.error("Error sending message:", chrome.runtime.lastError);
if (e.target === elements.modalOverlay) { } else {
elements.modalOverlay.classList.remove('show'); console.log("Response received:", response);
} }
}); clearStatusMessage();
} });
}
async function saveProxySourceSettings() { });
await sendMessage({ })
action: 'saveSettings', .catch((error) => {
settings: { statusMessage.textContent = `Proxy connection failed: ${error}. Settings not applied.`;
updateInterval: parseInt(elements.updateInterval.value, 10) statusMessage.style.color = "red";
} chrome.proxy.settings.clear({});
});
// Send a message indicating proxy setup failed
showToast('Proxy settings saved'); chrome.runtime.sendMessage({ action: "proxyFailed", error: error }, (response) => {
} if (chrome.runtime.lastError) {
console.error("Error sending message:", chrome.runtime.lastError);
async function saveBypassList() { } else {
const exceptions = elements.exceptions.value console.log("Response received for proxy failure:", response);
.split(',') }
.map(e => e.trim()) clearStatusMessage();
.filter(e => e.length > 0); });
})
await sendMessage({ .finally(() => {
action: 'saveSettings', // Re-enable the saveSettings button after the check is complete
settings: { saveSettings.disabled = false;
noProxyFor: exceptions isCheckingProxy = false;
} });
}); });
showToast('Bypass list saved', 'success'); disableProxy.addEventListener("click", () => {
} chrome.proxy.settings.clear({}, () => {
if (chrome.runtime.lastError) {
async function clearBypassList() { statusMessage.textContent = "Error disabling proxy: " + chrome.runtime.lastError.message;
elements.exceptions.value = ''; statusMessage.style.color = "red";
} else {
await sendMessage({ statusMessage.textContent = "Proxy has been disabled!";
action: 'saveSettings', statusMessage.style.color = "#e74c3c";
settings: { clearStatusMessage();
noProxyFor: [] console.log("Proxy settings disabled.");
} chrome.storage.local.get(["noProxyFor"], (result) => {
}); chrome.storage.local.set({
proxyType: null,
showToast('Bypass list cleared', 'success'); noProxyFor: result.noProxyFor,
} proxyEnabled: false
}, () => {
async function handleProxySourceChange() { if (chrome.runtime.lastError) {
const source = elements.proxySource.value; console.error("Error updating storage:", chrome.runtime.lastError);
} else {
await sendMessage({ chrome.runtime.sendMessage({ action: "disableProxy" });
action: 'saveSettings', }
settings: { });
proxySource: source });
} clearStatusMessage();
}); }
});
updateSourceNameDisplay(source); });
showToast('Proxy source changed');
} // Open Check Anyone page
checkAnyoneButton.addEventListener("click", () => {
function updateSourceNameDisplay(source) { window.open("https://check.en.anyone.tech/", "_blank");
const names = {
git: 'GitBros',
github: 'GitHub',
arweave: 'Arweave'
};
const urls = {
git: 'git.debros.io/DeBros/anyone-proxy-list',
github: 'github.com/DeBrosOfficial/anyone-proxy-list',
arweave: 'arweave.net/FjxfWIbS...B8'
};
const fullUrls = {
git: 'https://git.debros.io/DeBros/anyone-proxy-list',
github: 'https://github.com/DeBrosOfficial/anyone-proxy-list',
arweave: 'https://arweave.net/FjxfWIbSnZb7EaJWbeuWCsBBFWjTppfS3_KHxUP__B8'
};
const externalIcon = `<svg class="icon-external" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
<polyline points="15 3 21 3 21 9"/>
<line x1="10" y1="14" x2="21" y2="3"/>
</svg>`;
elements.sourceName.textContent = names[source] || 'GitBros';
elements.sourceUrl.innerHTML = (urls[source] || urls.git) + ' ' + externalIcon;
elements.sourceUrl.href = fullUrls[source] || fullUrls.git;
}
async function saveCustomProxy() {
const ip = elements.customIp.value.trim();
const port = elements.customPort.value.trim();
const username = elements.customUsername.value.trim();
const password = elements.customPassword.value;
// Validate
if (!ip) {
showToast('Please enter host address', 'error');
return;
}
if (!Utils.isValidHost(ip)) {
showToast('Invalid IP address or hostname', 'error');
return;
}
if (!port || !Utils.isValidPort(port)) {
showToast('Invalid port number (1-65535)', 'error');
return;
}
// Disable button during save
elements.btnSaveCustom.disabled = true;
const originalText = elements.btnSaveCustom.innerHTML;
elements.btnSaveCustom.innerHTML = '<span>Saving...</span>';
try {
// Save settings (bypass list is saved separately in Privacy section)
await sendMessage({
action: 'saveSettings',
settings: {
proxyIP: ip,
proxyPort: parseInt(port, 10),
proxyUsername: username,
proxyPassword: password
}
});
showToast('Custom proxy settings saved', 'success');
} finally {
elements.btnSaveCustom.disabled = false;
elements.btnSaveCustom.innerHTML = originalText;
}
}
async function clearCustomProxy() {
// Disable button during clear
elements.btnClearCustom.disabled = true;
const originalText = elements.btnClearCustom.innerHTML;
elements.btnClearCustom.innerHTML = '<span>Clearing...</span>';
try {
// Clear from storage first
const response = await sendMessage({
action: 'clearCustomProxy'
});
if (response.success) {
// Clear custom proxy UI fields
elements.customIp.value = '';
elements.customPort.value = '';
elements.customUsername.value = '';
elements.customPassword.value = '';
showToast('Custom proxy settings cleared', 'success');
} else {
showToast('Failed to clear settings', 'error');
}
} catch (error) {
showToast('Failed to clear settings', 'error');
} finally {
elements.btnClearCustom.disabled = false;
elements.btnClearCustom.innerHTML = originalText;
}
}
// ============================================
// Proxy Actions
// ============================================
async function refreshProxies() {
elements.btnRefreshProxies.disabled = true;
const originalText = elements.btnRefreshProxies.innerHTML;
elements.btnRefreshProxies.innerHTML = '<span>Refreshing...</span>';
const source = elements.proxySource.value || 'arweave';
const response = await sendMessage({ action: 'fetchProxies', source });
elements.btnRefreshProxies.disabled = false;
elements.btnRefreshProxies.innerHTML = originalText;
if (response.success) {
const sourceNames = { arweave: 'Arweave', git: 'GitBros', github: 'GitHub' };
const usedName = sourceNames[response.usedSource] || response.usedSource;
if (response.usedSource && response.usedSource !== source) {
showToast(`${response.proxies.length} proxies from ${usedName} (fallback)`, 'success');
// Update dropdown to show actual source used
elements.proxySource.value = response.usedSource;
updateSourceNameDisplay(response.usedSource);
} else {
showToast(`Updated: ${response.proxies.length} proxies`, 'success');
}
await updateProxySourceInfo();
} else {
showToast('All proxy sources failed', 'error');
}
}
async function updateProxySourceInfo() {
const response = await sendMessage({ action: 'getProxyList' });
if (response.success) {
const lastUpdate = response.lastUpdate;
if (lastUpdate) {
const relative = Utils.formatRelativeTime(lastUpdate);
elements.sourceUpdated.textContent = `Last updated: ${relative}`;
} else {
elements.sourceUpdated.textContent = 'Last updated: Never';
}
}
}
async function testCustomProxy() {
const ip = elements.customIp.value.trim();
const port = elements.customPort.value.trim();
const username = elements.customUsername.value.trim();
const password = elements.customPassword.value;
if (!ip || !port) {
showToast('Please enter IP and port', 'error');
return;
}
elements.btnTestCustom.disabled = true;
const originalText = elements.btnTestCustom.innerHTML;
elements.btnTestCustom.innerHTML = '<span>Testing...</span>';
// Get current connection state BEFORE testing
const statusBefore = await sendMessage({ action: 'getStatus' });
const wasConnected = statusBefore.enabled;
const previousMode = statusBefore.mode;
// Save credentials temporarily so auth handler can use them
if (username || password) {
await sendMessage({
action: 'saveSettings',
settings: {
proxyUsername: username,
proxyPassword: password
}
});
}
// 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,701 +1,188 @@
/* ANyONe Extension v2 - Popup Controller */ document.addEventListener("DOMContentLoaded", () => {
const proxyToggle = document.getElementById("proxyToggle");
const statusMessage = document.getElementById("statusMessage");
const optionsButton = document.getElementById("optionsButton");
const checkAnyoneButton = document.getElementById("checkAnyoneButton");
const dappStoreButton = document.getElementById("dappStoreButton");
const updateProxiesButton = document.getElementById("updateProxiesButton");
// ES Module imports if (!checkAnyoneButton) {
import { CONFIG } from './config.js'; checkAnyoneButton = document.createElement("button");
import { Utils } from './utils.js'; checkAnyoneButton.id = "checkAnyoneButton";
checkAnyoneButton.textContent = "Check ANyONe";
// ============================================ checkAnyoneButton.style.marginBottom = "10px";
// State checkAnyoneButton.addEventListener("click", () => {
// ============================================ 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'),
btnConnectText: document.getElementById('btn-connect-text'),
// 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);
elements.btnConnectText.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() { if (!dappStoreButton) {
console.log('[Popup] Loading proxy info...'); dappStoreButton = document.createElement("button");
const response = await sendMessage({ action: 'getProxyList' }); dappStoreButton.id = "dappStoreButton";
console.log('[Popup] Proxy list response:', response); dappStoreButton.textContent = "dApp Store";
dappStoreButton.style.marginBottom = "10px";
if (response.success) { dappStoreButton.addEventListener("click", () => {
state.proxyCount = response.count || 0; const storeUrl = chrome.runtime.getURL("store.html");
updateProxyInfo(); chrome.tabs.create({ url: storeUrl });
});
// Show warning if no proxies available
if (state.proxyCount === 0 && !state.connected && !state.connecting) {
showNoProxiesWarning();
} }
if (updateProxiesButton) {
updateProxiesButton.addEventListener("click", () => {
console.log("Update Proxies button clicked in popup.js");
statusMessage.textContent = "Updating proxies...";
statusMessage.style.color = "#f39c12"; // Orange for loading
chrome.runtime.sendMessage({ action: "updateProxies" }, (response) => {
if (response && response.success) {
console.log('Proxy list update was successful');
statusMessage.textContent = 'Proxies updated successfully!';
statusMessage.style.color = "#2ecc71"; // Green for success
} else { } else {
console.log('[Popup] Failed to load proxy info:', response.error); console.log('Proxy list update failed');
elements.proxyCount.textContent = 'Error loading'; statusMessage.textContent = 'Failed to update proxies.';
showNoProxiesWarning(); statusMessage.style.color = "#e74c3c"; // Red for failure
} }
} // Clear the update message after 3 seconds and reinitialize the UI
function updateProxyInfo() {
elements.proxyMode.textContent = 'Auto (Fastest)';
elements.proxyCount.textContent = `${state.proxyCount} nodes`;
}
function showNoProxiesWarning() {
elements.statusText.textContent = 'No proxies';
elements.statusText.style.color = 'var(--color-warning)';
elements.statusIp.textContent = 'Click Refresh to load proxy list';
elements.statusIp.style.display = 'block';
elements.statusIp.style.color = 'var(--color-text-muted)';
}
async function loadCustomProxy() {
const response = await sendMessage({ action: 'getSettings' });
if (response.success && response.settings) {
const settings = response.settings;
if (settings.proxyIP) {
elements.customIp.value = settings.proxyIP;
}
if (settings.proxyPort) {
elements.customPort.value = settings.proxyPort;
}
}
}
// ============================================
// Mode Switching
// ============================================
function switchMode(mode) {
state.mode = mode;
// Update tabs
elements.modeTabs.forEach(tab => {
tab.classList.toggle('active', tab.dataset.mode === mode);
});
// Update content
elements.modeContents.forEach(content => {
content.classList.toggle('active', content.id === `mode-${mode}`);
});
// Save mode
sendMessage({ action: 'setMode', mode });
}
function updateModeUI() {
// Activate correct tab and content
elements.modeTabs.forEach(tab => {
tab.classList.toggle('active', tab.dataset.mode === state.mode);
});
elements.modeContents.forEach(content => {
content.classList.toggle('active', content.id === `mode-${state.mode}`);
});
}
// ============================================
// Next Proxy (Load Balancing)
// ============================================
async function handleNextProxy() {
if (!state.connected) {
showError('Connect first to switch proxy');
return;
}
// Show switching state (orange)
state.connecting = true;
updateConnectionUI();
elements.statusText.textContent = 'Switching...';
elements.statusText.style.color = 'var(--color-warning)';
elements.statusIp.style.display = 'none';
elements.btnNextProxy.disabled = true;
elements.btnNextProxy.textContent = 'Switching...';
const response = await sendMessage({ action: 'nextProxy' });
state.connecting = false;
elements.statusText.style.color = '';
elements.btnNextProxy.disabled = false;
elements.btnNextProxy.textContent = 'Next Proxy';
if (response.success) {
state.currentProxy = response.proxy;
showSuccess(`Switched to ${response.proxy?.host || 'next proxy'}`);
updateConnectionUI();
} else {
showError(response.error || 'Failed to switch proxy');
}
}
// ============================================
// Connection
// ============================================
async function handleConnect() {
if (state.connecting) return;
if (state.connected) {
await disconnect();
} else {
await connect();
}
}
async function connect() {
state.connecting = true;
updateConnectionUI();
// If already connected, disconnect first before switching modes
if (state.connected) {
await sendMessage({ action: 'disconnect' });
state.connected = false;
state.currentProxy = null;
}
const options = {};
let customIp = null;
let customPort = null;
if (state.mode === 'public') {
options.country = 'auto';
}
// Save custom proxy before connecting if in custom mode
if (state.mode === 'custom') {
customIp = elements.customIp.value.trim();
customPort = elements.customPort.value.trim();
if (!customIp || !customPort) {
state.connecting = false;
showError('Please enter host and port');
return;
}
// Validate host and port
if (!Utils.isValidHost(customIp)) {
state.connecting = false;
showError('Invalid IP address or hostname');
return;
}
if (!Utils.isValidPort(customPort)) {
state.connecting = false;
showError('Invalid port (1-65535)');
return;
}
// Show testing state with IP:port
elements.statusText.textContent = 'Connecting...';
elements.statusText.style.color = 'var(--color-warning)';
elements.statusIp.textContent = `${customIp}:${customPort}`;
elements.statusIp.style.display = 'block';
// Save custom proxy settings
await sendMessage({
action: 'saveSettings',
settings: {
proxyIP: customIp,
proxyPort: parseInt(customPort, 10)
}
});
}
const response = await sendMessage({
action: 'connect',
mode: state.mode,
options
});
state.connecting = false;
elements.statusText.style.color = '';
if (response.success) {
state.connected = true;
state.currentProxy = response.proxy;
} else {
if (state.mode === 'custom') {
state.connected = false;
state.currentProxy = null;
showErrorPersistent(`Proxy ${customIp}:${customPort} is not responding`);
return;
}
showError(response.error || 'Connection failed');
}
updateConnectionUI();
}
async function disconnect() {
state.connecting = true;
updateConnectionUI();
const response = await sendMessage({ action: 'disconnect' });
state.connecting = false;
state.connected = !response.success;
state.currentProxy = null;
updateConnectionUI();
}
function updateConnectionUI() {
const btn = elements.btnConnect;
const btnText = elements.btnConnectText;
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;
btnText.disabled = true;
btnText.textContent = 'Connecting...';
} 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;
btnText.disabled = false;
btnText.textContent = 'Connect';
} else if (state.connected) {
btn.classList.add('connected');
card.classList.add('connected');
dot.classList.add('online');
statusText.textContent = 'Connected';
btn.disabled = false;
btnText.disabled = false;
btnText.textContent = 'Disconnect';
// 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;
btnText.disabled = false;
btnText.textContent = 'Connect';
}
}
// ============================================
// Custom Proxy
// ============================================
async function testCustomProxy() {
const ip = elements.customIp.value.trim();
const port = elements.customPort.value.trim();
if (!ip || !port) {
showError('Please enter host and port');
return;
}
// Validate host and port
if (!Utils.isValidHost(ip)) {
showError('Invalid IP address or hostname');
return;
}
if (!Utils.isValidPort(port)) {
showError('Invalid port (1-65535)');
return;
}
// Save previous state
const previousConnected = state.connected;
const previousProxy = state.currentProxy;
// Set connecting state (orange)
state.connecting = true;
updateConnectionUI();
elements.statusText.textContent = 'Testing...';
elements.statusText.style.color = 'var(--color-warning)';
elements.statusIp.textContent = `${ip}:${port}`;
elements.statusIp.style.display = 'block';
elements.btnTestCustom.disabled = true;
elements.btnTestCustom.textContent = 'Testing...';
const response = await sendMessage({
action: 'testProxy',
proxy: { host: ip, port: parseInt(port, 10) }
});
// Reset connecting state
state.connecting = false;
elements.statusText.style.color = '';
elements.btnTestCustom.disabled = false;
elements.btnTestCustom.textContent = 'Test Connection';
if (response.working) {
// Proxy is working, now actually connect to it
showSuccess(`Proxy ${ip}:${port} is working!`);
// Save custom proxy settings and connect
await sendMessage({
action: 'saveSettings',
settings: {
proxyIP: ip,
proxyPort: parseInt(port, 10)
}
});
// Actually connect through the proxy
const connectResponse = await sendMessage({
action: 'connect',
mode: 'custom'
});
if (connectResponse.success) {
state.connected = true;
state.currentProxy = connectResponse.proxy || { host: ip, port: parseInt(port, 10) };
}
updateConnectionUI();
} else {
// Stay in error state, don't restore
state.connected = false;
state.currentProxy = null;
showErrorPersistent(`Proxy ${ip}:${port} is not responding`);
}
}
// ============================================
// Utilities
// ============================================
async function refreshProxies() {
const refreshIcon = elements.btnRefresh.querySelector('.icon');
if (refreshIcon) {
refreshIcon.classList.add('spinning');
}
// Get the stored proxy source setting
const settings = await sendMessage({ action: 'getSettings' });
const source = settings.settings?.proxySource || 'arweave';
const response = await sendMessage({ action: 'fetchProxies', source });
if (refreshIcon) {
refreshIcon.classList.remove('spinning');
}
if (response.success) {
// Show which source was used (especially if fallback occurred)
const sourceNames = { arweave: 'Arweave', git: 'GitBros', github: 'GitHub' };
const usedName = sourceNames[response.usedSource] || response.usedSource;
if (response.usedSource && response.usedSource !== source) {
showSuccess(`${response.proxies.length} proxies (fallback: ${usedName})`);
} else {
showSuccess(`Updated: ${response.proxies.length} proxies`);
}
await loadProxyInfo();
} else {
showError('All sources failed');
}
}
function sendMessage(message) {
return new Promise((resolve) => {
chrome.runtime.sendMessage(message, (response) => {
resolve(response || { success: false, error: 'No response' });
});
});
}
function handleBackgroundMessage(message) {
console.log('[Popup] Background message:', message);
switch (message.action) {
case 'statusUpdate':
if (message.status === 'connected') {
state.connected = true;
state.connecting = false;
state.blocked = false;
state.currentProxy = message.proxy;
updateConnectionUI();
} else if (message.status === 'disconnected') {
state.connected = false;
state.connecting = false;
state.blocked = false;
state.currentProxy = null;
updateConnectionUI();
} else if (message.status === 'connecting') {
state.connecting = true;
state.blocked = false;
updateConnectionUI();
} else if (message.status === 'blocked') {
state.connected = false;
state.connecting = false;
state.blocked = true;
state.currentProxy = null;
updateConnectionUI();
} else if (message.status === 'error') {
state.connected = false;
state.connecting = false;
state.currentProxy = null;
// showError handles its own UI update, don't call updateConnectionUI
showError(message.error || 'Connection error');
}
break;
case 'proxyError':
showError(`Proxy error: ${message.error}`);
break;
case 'proxiesUpdated':
loadProxyInfo();
break;
}
}
function showError(message) {
console.log('[Popup] Error:', message);
// Reset state to disconnected
state.connected = false;
state.connecting = false;
state.currentProxy = null;
// Update status card
elements.statusCard.classList.remove('connected', 'connecting', 'disconnected');
elements.statusCard.classList.add('error');
// "Error" in red
elements.statusText.textContent = 'Error';
elements.statusText.style.color = 'var(--color-error)';
// Message below in default color
elements.statusIp.textContent = message;
elements.statusIp.style.display = 'block';
elements.statusIp.style.color = '';
// Update dot to error state
elements.statusDot.classList.remove('online', 'offline', 'connecting');
elements.statusDot.classList.add('error');
// Update button to error state
elements.btnConnect.classList.remove('connected', 'connecting');
elements.btnConnect.classList.add('error');
elements.btnConnect.disabled = false;
// After 3 seconds, just reset the error styling but keep showing "Disconnected" state
setTimeout(() => { setTimeout(() => {
elements.statusCard.classList.remove('error'); initializeUI();
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); }, 3000);
} });
});
}
function showErrorPersistent(message) { const buttonContainer = document.querySelector(".button-container");
console.log('[Popup] Error:', message); if (buttonContainer) {
buttonContainer.innerHTML = '';
buttonContainer.appendChild(optionsButton);
buttonContainer.appendChild(dappStoreButton);
buttonContainer.appendChild(updateProxiesButton);
}
// Update status card if (statusMessage && !checkAnyoneButton.parentNode) {
elements.statusCard.classList.remove('connected', 'connecting', 'disconnected'); statusMessage.parentNode.insertBefore(checkAnyoneButton, statusMessage.nextSibling);
elements.statusCard.classList.add('error'); }
// "Error" in red function updateStatusMessage(isEnabled, proxyType, proxy) {
elements.statusText.textContent = 'Error'; if (isEnabled) {
elements.statusText.style.color = 'var(--color-error)'; if (proxyType === "custom") {
statusMessage.textContent = `Custom Proxy is ENABLED and routing through ${proxy.host}:${proxy.port}`;
statusMessage.style.color = "#2ecc71"; // Green for custom
} else {
statusMessage.textContent = `Public Proxy is ENABLED and routing through ${proxy.host}:${proxy.port}`;
statusMessage.style.color = "#03bdc5"; // Blue for public
}
} else {
statusMessage.textContent = "Proxy is DISABLED";
statusMessage.style.color = "#e74c3c";
}
}
// Message below in default color function updateUI(isEnabled, proxyType, proxy) {
elements.statusIp.textContent = message; proxyToggle.checked = isEnabled;
elements.statusIp.style.display = 'block'; updateStatusMessage(isEnabled, proxyType, proxy);
elements.statusIp.style.color = ''; }
// Update button function updateStatusFromBackground(data) {
elements.btnConnect.classList.remove('connected', 'connecting'); statusMessage.textContent = data.message;
elements.btnConnect.classList.add('error'); statusMessage.style.color = data.color;
elements.btnConnect.disabled = false;
// Return to disconnected state after 5 seconds
setTimeout(() => { setTimeout(() => {
state.connected = false; statusMessage.textContent = "";
state.currentProxy = null; initializeUI(); // Reinitialize the UI to show the current status
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); }, 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();
proxyToggle.addEventListener("change", () => {
const isEnabled = proxyToggle.checked;
if (isEnabled) {
chrome.storage.local.get(["proxyType"], (data) => {
if (data.proxyType === "custom") {
chrome.storage.local.get(["proxyIP", "proxyPort"], (settings) => {
chrome.runtime.sendMessage({ action: "updateProxy", type: "custom", proxy: { host: settings.proxyIP, port: settings.proxyPort } }, (response) => {
if (response && response.status === "enabled") {
updateUI(true, "custom", { host: settings.proxyIP, port: settings.proxyPort });
} else {
alert("Failed to enable custom proxy. Please try again.");
proxyToggle.checked = false;
initializeUI();
}
});
});
} else {
chrome.runtime.sendMessage({ action: "enableProxy" }, (response) => {
if (response && response.status === "enabled" && response.proxy) {
chrome.storage.local.set({ proxyEnabled: true, currentProxy: response.proxy, proxyType: "public" });
updateUI(true, "public", response.proxy);
} else {
alert(response.message || "Failed to enable public proxy. Please try again.");
proxyToggle.checked = false;
initializeUI();
}
});
}
});
} else {
chrome.runtime.sendMessage({ action: "disableProxy" }, (response) => {
if (response && response.status === "disabled") {
chrome.storage.local.set({ proxyEnabled: false, proxyType: null });
updateUI(false);
} else {
alert("Failed to disable proxy. Please try again.");
proxyToggle.checked = true;
initializeUI();
}
});
}
});
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === "updatePopupState" || message.action === "disableProxy") {
initializeUI();
} else if (message.action === "showLoadingMessage") {
statusMessage.textContent = message.message;
statusMessage.style.color = "#f39c12"; // Orange for loading
} else if (message.action === "updateStatus") {
updateStatusFromBackground(message);
} else if (message.action === "toggleOff") {
// This is for when a proxy setup fails
proxyToggle.checked = false;
statusMessage.textContent = "Failed to set up proxy. Please check your settings.";
statusMessage.style.color = "#e74c3c"; // Red for error
// You might want to clear this message after some time or on interaction
setTimeout(() => {
statusMessage.textContent = "";
}, 5000); // Clear after 5 seconds
}
});
// Open options page
optionsButton.addEventListener("click", () => {
chrome.runtime.openOptionsPage();
});
// Open Check Anyone page
checkAnyoneButton.addEventListener("click", () => {
window.open("https://check.en.anyone.tech/", "_blank");
});
// Open dApp Store page
dappStoreButton.addEventListener("click", () => {
const storeUrl = chrome.runtime.getURL("html/store.html");
chrome.tabs.create({ url: storeUrl });
});
});

View File

@ -1,432 +0,0 @@
/* 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 };

View File

@ -1,257 +0,0 @@
/* 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 };

27
js/store.js Normal file
View File

@ -0,0 +1,27 @@
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);
}
});

View File

@ -1,273 +0,0 @@
/* 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,30 +1,21 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "ANyONe Extension", "name": "ANyONe Extension",
"version": "2.0.1", "version": "1.0.5",
"description": "Privacy-focused Socks5 proxy management", "description": "Manage Socks5 proxy settings",
"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": "ANyONe - Privacy Extension", "default_title": "Proxy Settings"
"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",