]] .. t_title .. [[
+
+]] .. t_status .. [[
+]] .. t_protected .. [[
+]] .. t_why .. [[
+]] .. t_why_desc .. [[
+diff --git a/.claude/.agent b/.claude similarity index 100% rename from .claude/.agent rename to .claude diff --git a/.gemini b/.gemini new file mode 120000 index 00000000..9b617313 --- /dev/null +++ b/.gemini @@ -0,0 +1 @@ +.agent \ No newline at end of file diff --git a/.gemini/skills/plan/SKILL.md b/.gemini/skills/plan/SKILL.md deleted file mode 100644 index a330489c..00000000 --- a/.gemini/skills/plan/SKILL.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -name: plan -description: 项目级技能:规定在开启新方案、新计划或进行任务交接时,必须将计划落库到 docs/plan 文件夹中并使用对应模板。 ---- - -# Plan & Handover Skill - -当你在当前项目中被要求“开启一个新的方案”、“制定开发计划”或者准备“任务交接(Handover)”时,你**必须**遵循本技能的工作流,将计划或方案落库到 `docs/plan/` 目录下。 - -> [!IMPORTANT] -> **什么时候应当创建实现计划?** -> * **必须创建的场景**:新功能开发、涉及多组件的重大架构重构、引入新基础设施依赖,以及存在显著设计决策冲突的**中大型、复杂**需求。 -> * **绝对不要创建的场景**:改个包名、挪个文件、重命名函数、小修小改修复 Bug 等**轻量级、简单的局部重构**。对于此类改动,应当直接完成并运行单元测试通过后交付,禁止制造冗余的计划文档。 - -## 执行工作流 (Workflow) - -### 1. 确定计划类型 -* **新特性/技术实现计划**:如果你要开发新功能或进行重大重构,你需要创建**实现计划**。 -* **AI 任务交接计划**:如果当前任务尚未完成但需要记录进度留作以后或其他 AI 代理接手,你需要创建**交接计划**。 - -### 2. 读取对应模板 -在创建计划文档前,必须读取对应的模板内容,并严格按照模板的骨架进行填充: -* **实现计划模板**:`docs/plan/implementation-plan-template.md` -* **接手计划模板**:`docs/plan/handover-plan-template.md` - -### 3. 落库与命名规范 -在 `docs/plan/` 目录下创建新的 Markdown 文件进行保存: -* **实现计划**命名格式:`docs/plan/YYYYMMDD-[feature-name].md` (例如:`20260605-uptime-kuma-sync.md`) -* **接手计划**命名格式:`docs/plan/handover-[task-name].md` (例如:`handover-waf-ip-group.md`) - -### 4. 隔离约束 (极其重要) -`docs/plan/` 目录下的文档**仅限内部开发和 AI 代理同步使用**。 -* **绝对禁止**将新创建的 plan 文档加入到项目的官方导航配置(如 `docs/config.ts` 的 `nav` 或 `sidebar` 导航条中)。 -* **绝对禁止**通过任何方式将其暴露给文档渲染框架(如 VitePress)对外渲染。 - -## 后续动作 -落库完成后,向用户报告计划已生成在 `docs/plan/` 目录下,并列出文档的核心要点或待决策项(如有),等待用户 Review 或批准后即可推进下一步。 diff --git a/Claude.md b/Claude.md new file mode 120000 index 00000000..96808718 --- /dev/null +++ b/Claude.md @@ -0,0 +1 @@ +Agents.md \ No newline at end of file diff --git a/data/agent/etc/nginx/conf.d/openflare_routes.conf b/data/agent/etc/nginx/conf.d/openflare_routes.conf new file mode 100644 index 00000000..7f9a5cda --- /dev/null +++ b/data/agent/etc/nginx/conf.d/openflare_routes.conf @@ -0,0 +1,25 @@ +# This file is generated by OpenFlare. Do not edit manually. +upstream backend_local_1 { + server flare.arctel.net:443 max_fails=3 fail_timeout=10s; + keepalive 128; +} + +server { + listen 80; + server_name a.com; + set $openflare_waf_site "local"; + access_by_lua_file /data/etc/nginx/lua/waf/check.lua; + location / { + proxy_set_header Host $host; + proxy_ssl_server_name on; + proxy_ssl_name "flare.arctel.net"; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_http_version 1.1; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Upgrade $http_upgrade; + proxy_pass https://backend_local_1; + } +} + diff --git a/data/agent/etc/nginx/lua/init.lua b/data/agent/etc/nginx/lua/init.lua new file mode 100644 index 00000000..cdb98000 --- /dev/null +++ b/data/agent/etc/nginx/lua/init.lua @@ -0,0 +1,6 @@ +local dict = ngx.shared.openflare_observability +if not dict then + return +end + +return diff --git a/data/agent/etc/nginx/lua/log.lua b/data/agent/etc/nginx/lua/log.lua new file mode 100644 index 00000000..20daf1a1 --- /dev/null +++ b/data/agent/etc/nginx/lua/log.lua @@ -0,0 +1,82 @@ +local dict = ngx.shared.openflare_observability +if not dict then + return +end + +local request_uri = tostring(ngx.var.uri or "") +if request_uri == "/openflare/observability" or request_uri == "/openflare/stub_status" then + return +end + +local ttl = 7200 +local now = ngx.time() +local window_size = 60 +local window_start = now - (now % window_size) + +local function ensure_counter(key) + dict:add(key, 0, ttl) +end + +local function incr(key, delta) + ensure_counter(key) + local value, err = dict:incr(key, delta) + if not value and err == "not found" then + dict:set(key, delta, ttl) + end +end + +local function remember_value(list_key, marker_key, value) + if value == "" then + return + end + if not dict:add(marker_key, 1, ttl) then + return + end + local existing = dict:get(list_key) + if not existing or existing == "" then + dict:set(list_key, value, ttl) + return + end + dict:set(list_key, existing .. "\n" .. value, ttl) +end + +local window_prefix = tostring(window_start) +incr("request_count:" .. window_prefix, 1) + +local status = tostring(ngx.status or 0) +if status ~= "0" then + incr("status:" .. window_prefix .. ":" .. status, 1) + remember_value( + "status_keys:" .. window_prefix, + "status_marker:" .. window_prefix .. ":" .. status, + status + ) + if tonumber(status) and tonumber(status) >= 500 then + incr("error_count:" .. window_prefix, 1) + end +end + +local host = tostring(ngx.var.host or "") +if host ~= "" then + incr("domain:" .. window_prefix .. ":" .. host, 1) + remember_value( + "domain_keys:" .. window_prefix, + "domain_marker:" .. window_prefix .. ":" .. host, + host + ) +end + +local remote_addr = tostring(ngx.var.binary_remote_addr or ngx.var.remote_addr or "") +if remote_addr ~= "" and dict:add("visitor:" .. window_prefix .. ":" .. remote_addr, 1, ttl) then + incr("unique_visitor_count:" .. window_prefix, 1) +end + +local request_length = tonumber(ngx.var.request_length) or 0 +if request_length > 0 then + incr("openresty_rx_bytes:" .. window_prefix, request_length) +end + +local bytes_sent = tonumber(ngx.var.bytes_sent) or tonumber(ngx.var.body_bytes_sent) or 0 +if bytes_sent > 0 then + incr("openresty_tx_bytes:" .. window_prefix, bytes_sent) +end diff --git a/data/agent/etc/nginx/lua/observability/init.lua b/data/agent/etc/nginx/lua/observability/init.lua new file mode 100644 index 00000000..cdb98000 --- /dev/null +++ b/data/agent/etc/nginx/lua/observability/init.lua @@ -0,0 +1,6 @@ +local dict = ngx.shared.openflare_observability +if not dict then + return +end + +return diff --git a/data/agent/etc/nginx/lua/observability/log.lua b/data/agent/etc/nginx/lua/observability/log.lua new file mode 100644 index 00000000..20daf1a1 --- /dev/null +++ b/data/agent/etc/nginx/lua/observability/log.lua @@ -0,0 +1,82 @@ +local dict = ngx.shared.openflare_observability +if not dict then + return +end + +local request_uri = tostring(ngx.var.uri or "") +if request_uri == "/openflare/observability" or request_uri == "/openflare/stub_status" then + return +end + +local ttl = 7200 +local now = ngx.time() +local window_size = 60 +local window_start = now - (now % window_size) + +local function ensure_counter(key) + dict:add(key, 0, ttl) +end + +local function incr(key, delta) + ensure_counter(key) + local value, err = dict:incr(key, delta) + if not value and err == "not found" then + dict:set(key, delta, ttl) + end +end + +local function remember_value(list_key, marker_key, value) + if value == "" then + return + end + if not dict:add(marker_key, 1, ttl) then + return + end + local existing = dict:get(list_key) + if not existing or existing == "" then + dict:set(list_key, value, ttl) + return + end + dict:set(list_key, existing .. "\n" .. value, ttl) +end + +local window_prefix = tostring(window_start) +incr("request_count:" .. window_prefix, 1) + +local status = tostring(ngx.status or 0) +if status ~= "0" then + incr("status:" .. window_prefix .. ":" .. status, 1) + remember_value( + "status_keys:" .. window_prefix, + "status_marker:" .. window_prefix .. ":" .. status, + status + ) + if tonumber(status) and tonumber(status) >= 500 then + incr("error_count:" .. window_prefix, 1) + end +end + +local host = tostring(ngx.var.host or "") +if host ~= "" then + incr("domain:" .. window_prefix .. ":" .. host, 1) + remember_value( + "domain_keys:" .. window_prefix, + "domain_marker:" .. window_prefix .. ":" .. host, + host + ) +end + +local remote_addr = tostring(ngx.var.binary_remote_addr or ngx.var.remote_addr or "") +if remote_addr ~= "" and dict:add("visitor:" .. window_prefix .. ":" .. remote_addr, 1, ttl) then + incr("unique_visitor_count:" .. window_prefix, 1) +end + +local request_length = tonumber(ngx.var.request_length) or 0 +if request_length > 0 then + incr("openresty_rx_bytes:" .. window_prefix, request_length) +end + +local bytes_sent = tonumber(ngx.var.bytes_sent) or tonumber(ngx.var.body_bytes_sent) or 0 +if bytes_sent > 0 then + incr("openresty_tx_bytes:" .. window_prefix, bytes_sent) +end diff --git a/data/agent/etc/nginx/lua/observability/read.lua b/data/agent/etc/nginx/lua/observability/read.lua new file mode 100644 index 00000000..fe15ecc4 --- /dev/null +++ b/data/agent/etc/nginx/lua/observability/read.lua @@ -0,0 +1,45 @@ +local cjson = require "cjson.safe" + +local dict = ngx.shared.openflare_observability +if not dict then + ngx.status = ngx.HTTP_SERVICE_UNAVAILABLE + ngx.say(cjson.encode({ message = "shared dict unavailable" })) + return +end + +local now = ngx.time() +local window_size = 60 +local window_start = now - (now % window_size) +local current_window = tostring(window_start) + +local function read_counter(key) + return tonumber(dict:get(key) or 0) or 0 +end + +local function read_map(window_id, prefix, list_key) + local result = {} + local raw = dict:get(list_key .. ":" .. window_id) + if not raw or raw == "" then + return result + end + for value in string.gmatch(raw, "[^\n]+") do + result[value] = read_counter(prefix .. ":" .. window_id .. ":" .. value) + end + return result +end + +local payload = { + window_started_at_unix = window_start, + window_ended_at_unix = now, + request_count = read_counter("request_count:" .. current_window), + error_count = read_counter("error_count:" .. current_window), + unique_visitor_count = read_counter("unique_visitor_count:" .. current_window), + status_codes = read_map(current_window, "status", "status_keys"), + top_domains = read_map(current_window, "domain", "domain_keys"), + source_countries = {}, + openresty_rx_bytes = read_counter("openresty_rx_bytes:" .. current_window), + openresty_tx_bytes = read_counter("openresty_tx_bytes:" .. current_window) +} + +ngx.header.content_type = "application/json" +ngx.say(cjson.encode(payload)) diff --git a/data/agent/etc/nginx/lua/pow/challenge.lua b/data/agent/etc/nginx/lua/pow/challenge.lua new file mode 100644 index 00000000..c793c6f1 --- /dev/null +++ b/data/agent/etc/nginx/lua/pow/challenge.lua @@ -0,0 +1,134 @@ +local cjson = require "cjson.safe" + +local pow_config_dict = ngx.shared.openflare_pow_config +local pow_challenges = ngx.shared.openflare_pow_challenges + +local function generate_entropy() + local pieces = { + tostring(ngx.now()), + tostring(ngx.worker.pid()), + tostring(math.random()), + ngx.var.remote_addr or "", + ngx.var.http_user_agent or "", + ngx.var.request_id or "", + } + return table.concat(pieces, ":") +end + +local args = ngx.req.get_uri_args() +local host = args["host"] or ngx.var.host or "" +local redir = args["redir"] or "" + +local site = ngx.var.openflare_waf_site or "" +if site == "" then + site = host +end + +local config_raw = pow_config_dict:get(site) +if not config_raw then + ngx.status = 403 + ngx.say("PoW not configured for this site") + return +end + +local ok, route_config = pcall(cjson.decode, config_raw) +if not ok or not route_config or not route_config.enabled then + ngx.status = 403 + ngx.say("PoW not enabled for this site") + return +end + +local config = route_config.config or {} +local difficulty = config.difficulty or 4 +local algorithm = config.algorithm or "fast" +local challenge_ttl = config.challenge_ttl or 300 +local session_ttl = config.session_ttl or 600 + +-- Generate challenge data without depending on ngx.random_bytes, which is not +-- available in every OpenResty runtime build. +local entropy = generate_entropy() +local challenge_id = ngx.md5(entropy .. ":id") +local challenge_data = ngx.md5(entropy .. ":data-a") .. ngx.md5(entropy .. ":data-b") + +-- Store challenge +local challenge_info = cjson.encode({ + data = challenge_data, + difficulty = difficulty, + host = host, + redir = redir, + session_ttl = session_ttl +}) +pow_challenges:set(challenge_id, challenge_info, challenge_ttl) + +local static_prefix = "/.within.website/x/cmd/anubis/static/" +local accept_lang = ngx.var.http_accept_language or "" +local lang = "en" +if string.find(accept_lang, "zh") then + lang = "zh-CN" +end + +local t_title = "Making sure you're not a bot!" +local t_status = "Loading..." +local t_protected = "This site is protected by a Proof-of-Work challenge. Your browser will solve a small puzzle before the upstream response is shown." +local t_why = "Why am I seeing this?" +local t_why_desc = "OpenFlare is asking your browser to complete a lightweight computation to distinguish normal browser traffic from automated abuse. This should finish automatically." +local t_noscript = "JavaScript is required to pass this verification. Please enable JavaScript and reload." + +if lang == "zh-CN" then + t_title = "正在确认你是不是机器人!" + t_status = "加载中..." + t_protected = "本网站受工作量证明(Proof-of-Work)挑战保护。在显示源站响应之前,您的浏览器将解决一个微型谜题。" + t_why = "为什么我会看到这个?" + t_why_desc = "OpenFlare 正在要求您的浏览器完成一项轻量级计算,以区分正常的浏览器流量和自动化的恶意请求。这应该会自动完成。" + t_noscript = "很遗憾,您必须启用 JavaScript 才能通过这项验证。请开启 JavaScript 并刷新页面。" +end + +ngx.header.content_type = "text/html; charset=utf-8" +ngx.say([[ + +
+ + + +
+]] .. t_status .. [[
+]] .. t_protected .. [[
+]] .. t_why_desc .. [[
+
+
+**No client-side software or plugin is required. You can run a network speed test from any device with a [Web Browser that is IE10 or newer.](https://www.youtube.com/watch?v=9f-OM_WQ7Bw&list=PLt-deStxFJOMEAs2O1lJhscMNzcg9E3Po&index=1)**
+
+
+
+
+## Why OpenSpeedTest
+
+
+
+### Secure by Design.
+
+
+
+OpenSpeedTest contains Only `STATIC` Files like `HTML`,`CSS` & `JS`.
+
+So you don't need to worry about Security Updates or Hidden Exploits that may compromise your secure environments.
+
+
+
+### Lightweight, High Performance.
+
+
+
+OpenSpeedTest is written in Vanilla JavaScript. No Third-Party frameworks or libraries were used. SpeedTest script file size is under 8kB gzip. The unexpected side effect of using Vanilla JavaScript is High Performance.
+
+
+
+### Run a speed test from Any Device.
+
+
+
+OpenSpeedTest will run on Any Web Browser that is IE10 or newer.
+
+
+
+### Ready for Any Display Size and Resolution.
+
+
+
+OpenSpeedTest User interface is written in SVG.
+
+
+
+# Create Your Own SpeedTest Server.
+
+### Server Requirements :
+
+`Nginx`, `Apache`, `IIS`, `Express`, or Any Web server that supports `HTTP/1.1` or newer.
+
+- Accept, `GET`, `POST`, `HEAD` & `OPTIONS`, Response `200 OK`.
+
+- Accept, `POST` to Static Files, Response `200 OK`.
+
+- `client_max_body_size`, 35 Megabytes or more.
+
+- Timeout greater than `60 seconds`.
+
+- Disable `Access logs` for Increasing server performance.
+
+- Improve `Time to First Byte` (TTFB)
+
+- Warning! If you run it behind a **[Reverse Proxy](https://github.com/openspeedtest/Speed-Test/issues/4#issuecomment-1229157193)**, you should increase the `post-body content length` to 35 megabytes.
+- Supports `HTTP2` & `HTTP3`.
+- `HTTP1.1` is recommended for achieving maximum performance.
+- **[You Should Follow our Nginx Config.](https://github.com/openspeedtest/Nginx-Configuration)**
+
+
+
+# Or, You can use OpenSpeedTest-Server.
+
+OpenSpeedTest-Server is available for Windows, Mac, Linux, Android, iOS & Docker.
+[](https://go.openspeedtest.com/Server "Download OpenSpeedTest-Server V2.1")
+#### Fully Optimized and ready to use applications.
+
+
+
+### New features:
+
+
+
+1. Stress Test. (Continuous Speed Test)
+
+
+
+To enable the stress test. Pass `Stress` or `S` keyword as a URL parameter.
+
+
+
+````
+
+http://192.168.1.5?Stress=Low
+
+````
+
+After the `STRESS` or `S` keyword, you can specify the number of seconds you need to run the StressTest in seconds, or preset values such as `Low`, `Medium`, `High`, `VeryHigh`, `Extreme`, `Day`, and `Year`. Will run a speed test for `300`,`600`,`900`,`1800`,`3600`,`86400`,`31557600` seconds, respectively. Also, you can feed the first letter of each parameter and its values.
+
+
+````
+
+http://192.168.1.5?S=L
+
+````
+
+`S=L` is the same as passing `Stress=low`
+
+Or you can specify the number of seconds eg:`5000` directly without any preset keywords.
+
+
+
+````
+
+http://192.168.1.5?Stress=5000
+
+````
+
+
+
+2. Run a speed test automatically
+
+
+
+Run a speed test automatically on page load.
+
+````
+
+http://192.168.1.5?Run
+
+````
+
+Run a speed test automatically after a few seconds.
+
+````
+
+http://192.168.1.5?Run=10 or http://192.168.1.5?R=10
+
+````
+
+
+
+You can pass multiple keywords, and it's not `Case-Sensitive`.
+
+
+
+````
+
+http://192.168.1.5?Run&Stress=300 OR http://192.168.1.5?R&S=300
+
+````
+
+This will start a speed test immediately and run for `300 seconds` in each direction. That is 300 seconds for download and `300 seconds` for upload.
+
+
+
+3. Save results to a Database
+
+
+
+Edit `Index.html`
+
+````
+
+var saveData = true;
+
+var saveDataURL = "//yourDatabase.Server.com:4500/save?data=";
+
+````
+
+4. Add multiple servers. The app will choose one with the least latency automatically.
+
+
+
+Edit `Index.html`
+
+````
+
+var openSpeedTestServerList = [
+
+{"ServerName":"Home-Earth", "Download":"/downloading", "Upload":"/upload", "ServerIcon":"DefaultIcon"},
+
+{"ServerName":"Home-Mars", "Download":"/downloading", "Upload":"/upload", "ServerIcon":"DefaultIcon"},
+
+{"ServerName":"Home-Moon", "Download":"/downloading", "Upload":"/upload", "ServerIcon":"DefaultIcon"}
+
+];
+
+````
+
+5. Disable or change Overhead Compensation factor.
+
+
+
+````
+
+http://192.168.1.5?clean
+
+````
+
+Overhead Compensation factor, This is browser based test, Many Unknowns. Currently 4%. That is within the margin of error.
+
+You can pass `Clean` or `C` as a URL Parameter and reset Overhead Compensation factor to Zero or set any value between 0 and 4. 1 = 1% to 4 = 4%.
+
+`Clean` will not accept values above 4, so Compensation is limited to maximum 4%.
+
+
+
+6. Change the default limit of 6 parallel HTTP connections to the Server.
+
+
+
+````
+
+http://192.168.1.5?XHR=3 OR http://192.168.1.5?X=3
+
+````
+
+Allow the user to Change the default limit of 6 parallel HTTP connections to the Server. `XHR` will Accept values above 1 and max 32
+
+pass `XHR` or `X` as a URL Parameter.
+
+
+
+7. Select a different server to run a speed test.
+
+
+
+````
+
+http://192.168.1.5?Host=http://192.168.55.1:90 OR http://192.168.1.5?h=http://192.168.55.1:90
+
+````
+
+Pass `Host` or `H` as a URL Parameter.
+
+`HOST` will Accept only valid HTTP URLs like `http://192.168.1.10:3000` or `https://yourHost.com`.
+
+
+
+8. Select and run one test at a time, `DOWNLOAD`, `UPLOAD`, or `PING`.
+
+
+
+````
+
+http://192.168.1.5?Test=Upload OR http://192.168.1.5?T=U
+
+````
+
+`TEST` Allow the user to select and run one test at a time, Download, Upload, or Ping.
+
+Pass `Test` or `T` as a URL Parameter.
+
+
+
+9. Set a PingTimeout dynamically by passing `Out` or `O` as a URL Parameter
+
+
+
+````
+
+http://192.168.1.5?Out=7000 OR http://192.168.1.5?O=7000
+
+````
+
+If Server not responded within 5 Seconds for any requests we send ('pingSamples' times)
+
+We will show `Network Error`, You can change the limit here.
+
+In milliseconds, if you need to set `6 seconds`. Change the value to `6000`.
+
+
+
+10. Set the Number of ping samples by adding `Ping` or `P` as a URL Parameter
+
+
+
+````
+
+http://192.168.1.5?Ping=500 OR http://192.168.1.5?P=500
+
+````
+
+More samples = more accurate representation. `Ping = 500` will send `501` requests to server to find the accurate ping value.
+Take a look at index.html, you can set a custom ping sample size, threads, upload data size etc.
+
+## Self-hosted (On-Premise) / (Docker Image/Source Code)
+### For Headless large-scale deployments.
+You have two options here. If you need a custom deployment, use our source code along with a web server of your choice. I prefer Nginx, and you can find my [Nginx Configuration](https://github.com/openspeedtest/Nginx-Configuration) here. Or you can use our docker image. You can deploy it on your LAN/WAN with or without an active internet connection.
+
+**This is docker implementation using nginxinc/nginx-unprivileged:stable-alpine. uses significantly fewer resources.**
+
+- NGINX Docker image that runs NGINX as a non root, unprivileged user.
+
+ ### Docker install instructions:
+
+ Install Docker and run the following command!
+
+````bash
+
+sudo docker run --restart=unless-stopped --name openspeedtest -d -p 3000:3000 -p 3001:3001 openspeedtest/latest
+
+````
+#### Or use docker-compose.yml
+````
+version: '3.3'
+services:
+ speedtest:
+ restart: unless-stopped
+ container_name: openspeedtest
+ ports:
+ - '3000:3000'
+ - '3001:3001'
+ image: openspeedtest/latest
+````
+- Warning! If you run it behind a **[Reverse Proxy](https://github.com/openspeedtest/Speed-Test/issues/4#issuecomment-1229157193)**, you should increase the `post-body content length` to 35 megabytes.
+
+- **[Follow our Nginx Config.](https://github.com/openspeedtest/Nginx-Configuration)**
+
+Now open your browser and direct it to:
+
+A: For **HTTP** use: `http://YOUR-SERVER-IP:3000`
+
+B: For **HTTPS** use: `https://YOUR-SERVER-IP:3001`
+
+#### Container-Port for http is 3000
+If you need to run this image on a different port for `HTTP`, Eg: change to `80` = `-p 80:3000`
+#### Container-Port for https is 3001
+If you need to run this image on a different port for `HTTPS`, Eg: change to `443` = `-p 443:3001`
+
+### Setup Free LetsEncrypt SSL with Automatic Certificate Renewal
+***Requirements***
+- PUBLIC IPV4 and/or IPV6 address.
+- A domain name that resolves to speed test server's IP address.
+- Email ID
+
+The following command will generate a Let's Encrypt certificate for your domain name and configure a cron job to automatically renew the certificate.
+
+````
+docker run -e ENABLE_LETSENCRYPT=True -e DOMAIN_NAME=speedtest.yourdomain.com -e USER_EMAIL=you@yourdomain.pro --restart=unless-stopped --name openspeedtest -d -p 80:3000 -p 443:3001 openspeedtest/latest
+````
+#### Or use docker-compose.yml
+````
+version: '3.3'
+services:
+ speedtest:
+ environment:
+ - ENABLE_LETSENCRYPT=True
+ - DOMAIN_NAME=speedtest.yourdomain.com
+ - USER_EMAIL=you@yourdomain.pro
+ restart: unless-stopped
+ container_name: openspeedtest
+ ports:
+ - '80:3000'
+ - '443:3001'
+ image: openspeedtest/latest
+````
+
+### How to Use Your Own Secure Sockets Layer (SSL) Certificate, Self-Signed or Paid?
+***Requirements***
+- Folder with your Certificate, Self-Signed or Paid.
+- Rename .cet file and .key file to `nginx.crt` & `nginx.key`
+
+ The folder needs to contain:
+
+- `nginx.crt`
+
+- `nginx.key`
+
+
+````
+sudo docker run --restart=unless-stopped --name openspeedtest -d -p 3000:3000 -p 3001:3001 openspeedtest/latest
+````
+
+To mount a folder with your own SSL certificate to this Docker container, append the following line to the above command:
+
+
+````bash
+
+-v /${PATH-TO-YOUR-OWN-SSL-CERTIFICATE}:/etc/ssl/
+
+````
+
+I am adding a folder with nginx.crt and nginx.key from my desktop by using the following command.
+
+````bash
+
+sudo docker run -v /Users/vishnu/Desktop/docker/:/etc/ssl/ --restart=unless-stopped --name openspeedtest -d -p 3000:3000 -p 3001:3001 openspeedtest/latest
+
+````
+#### Or use docker-compose.yml
+````
+version: '3.3'
+services:
+ speedtest:
+ volumes:
+ - '/Users/vishnu/Desktop/docker/:/etc/ssl/'
+ restart: unless-stopped
+ container_name: openspeedtest
+ ports:
+ - '3000:3000'
+ - '3001:3001'
+ image: openspeedtest/latest
+````
+## Advanced Configuration Options
+
+- Container Port Configuration
+
+To enable port changes, set the `CHANGE_CONTAINER_PORTS` environment variable to `"True"` and provide appropriate values for the following variables.
+
+`CHANGE_CONTAINER_PORTS=True`
+
+`HTTP_PORT=3000`
+
+`HTTPS_PORT=3001`
+
+- Set User
+
+`SET_USER=101`
+
+- Only Allow `CORS Request` from listed domains.
+
+`ALLOW_ONLY=domain1.com;domain2.com;domain3.com`
+
+- `SET_SERVER_NAME` Display the server name on the UI.
+
+`SET_SERVER_NAME=HOME-NAS`
+
+
+Docker images run better on Linux Platforms, including your NAS. But if you install docker on macOS or Windows, you may see poor performance. I asked this on Docker forums, and they told me macOS and Windows support is for Development purposes only. For Production, you need to use any Linux Platform.
+
+The same Story goes for Windows NGINX. Nginx uses only one worker even if you specify n number of worker processes. They will show in Task Manager, but the system will only use one. I got this information directly from the Nginx website.
+
+
+
+
+
+## Why do you need to Create Your Own SpeedTest Server?
+
+You can run OpenSpeedTest Server in your Home Lab, Office Server or Cloud Server. So that you or employees who work from home can run a speed test to your office and make sure they can run everything smoothly.
+
+
+
+**Choosing between ISP1 & ISP2.**
+
+
+
+Sometimes your ISP2 is Faster than ISP1 when you test your speed on popular speed test sites. But when you connect to your Home/Office/Cloud, that slower connection may perform better. The only way to find out is to run a speed test against your infrastructure.
+
+
+
+**Troubleshooting network issues.**
+
+
+
+It is common even when your Internet connection is working fine, but some of the devices in your network may experience trouble getting decent connectivity to the internet. The issue might be the wrong VLAN ID or Faulty Switch. If you run a Local network speed test, you can find and fix these issues easily.
+
+
+
+**Before you add a repeater.**
+
+
+
+Most repeaters will reduce your network speed by 50%, so if you put it far away, it will perform worst, and if you put it too close, you will not get enough coverage if you run a Local Network speed test. Depending on the application requirements, you can decide exactly where you need to put your repeater.
+
+
+
+**Browsing experience.**
+
+
+
+Many useful browser extensions are out there that we all know and love. But some of them are really slowing you down for **few seconds per page you visit**. You may see good performance when you test your network performance via File Transfer or Command-line utilities, but you may experience poor performance when browsing the internet. This is due to a bad browser configuration that including unwanted extensions installed. From my experience, only keep the one you are going to use every single day. Extension that you may use once in a while should be removed or disabled for maximum performance. If you see poor performance, try OpenSpeedTest from Private Window or Incognito Window. **This tool can be used to check the browser performance and impact of Extensions on your browsing experience.**
+
+
+
+**No client-side software or plugin is required. You can run a network speed test from any device with a [Web Browser that is IE10 or newer.](https://www.youtube.com/watch?v=9f-OM_WQ7Bw&list=PLt-deStxFJOMEAs2O1lJhscMNzcg9E3Po&index=1)**
+
+- Like this Project? Please **Donate NOW & Keep us Alive** -> https://go.openspeedtest.com/Donate
+
+MIT License
+
+Copyright (c) 2013 - 2023 OpenSpeedTest™
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/data/agent/var/lib/openflare/pages/deployments/1/current/assets/css/app.css b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/css/app.css
new file mode 100644
index 00000000..39fa9393
--- /dev/null
+++ b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/css/app.css
@@ -0,0 +1,382 @@
+/* roboto-regular - latin */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+ src: url("..fonts/roboto-v30-latin-regular.eot"); /* IE9 Compat Modes */
+ src: local(""),
+ url("../fonts/roboto-v30-latin-regular.eot?#iefix")
+ format("embedded-opentype"),
+ /* IE6-IE8 */ url("../fonts/roboto-v30-latin-regular.woff2")
+ format("woff2"),
+ /* Super Modern Browsers */
+ url("../fonts/roboto-v30-latin-regular.woff") format("woff"),
+ /* Modern Browsers */ url("../fonts/roboto-v30-latin-regular.ttf")
+ format("truetype"),
+ /* Safari, Android, iOS */
+ url("../fonts/roboto-v30-latin-regular.svg#Roboto") format("svg"); /* Legacy iOS */
+}
+/* roboto-500 - latin */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 500;
+ font-display: swap;
+ src: url("assets/fonts/roboto-v30-latin-500.eot"); /* IE9 Compat Modes */
+ src: local(""),
+ url("assets/fonts/roboto-v30-latin-500.eot?#iefix")
+ format("embedded-opentype"),
+ /* IE6-IE8 */ url("../fonts/roboto-v30-latin-500.woff2")
+ format("woff2"),
+ /* Super Modern Browsers */ url("../fonts/roboto-v30-latin-500.woff")
+ format("woff"),
+ /* Modern Browsers */ url("../fonts/roboto-v30-latin-500.ttf")
+ format("truetype"),
+ /* Safari, Android, iOS */
+ url("../fonts/roboto-v30-latin-500.svg#Roboto") format("svg"); /* Legacy iOS */
+}
+
+body {
+ margin: 0px;
+ padding: 0px;
+ display: block;
+}
+
+::-webkit-scrollbar {
+ display: none;
+}
+html {
+ -ms-overflow-style: none;
+ scrollbar-width: none;
+}
+
+.Credits {
+color: rgb(125 119 119);
+text-align: center;
+font-size:14px;
+font-family: Roboto-Medium, Roboto;
+font-weight: 500;
+}
+.Credits a {
+ text-decoration: none;
+ color: rgb(113, 113, 113);
+}
+.Credits a:hover {
+color: #14b0fe;
+}
+
+.ConnectError {
+ display: none;
+
+}
+
+.openSpeedtestApp {
+
+ height: 100vh;
+ width: 100vw;
+ display: none;
+ overflow: hidden;
+}
+.main-Gaugebg {
+ fill: none;
+ stroke: rgb(231, 231, 232);
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-width: 22px;
+ stroke-dasharray: 681;
+}
+.main-GaugeBlue {
+ fill: none;
+ stroke: url(#gradient);
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-width: 22px;
+ stroke-dasharray: 681;
+ stroke-opacity: 0;
+}
+.main-GaugeWhite {
+ fill: none;
+ stroke: rgb(255, 255, 255);
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-width: 15px;
+ stroke-dasharray: 0, 681;
+ stroke-dashoffset: 1;
+ stroke-opacity: 0;
+}
+.oDo-Meter {
+ font-size: 16.633283615112305px;
+ fill: gray;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+}
+.oDoLive-Speed {
+ font-size: 28px;
+ fill: #201e1e;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+
+.oDoLive-Status {
+ font-size: 10px;
+ fill: #d2d1d2;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+.uiBg {
+ fill: #d2d1d2;
+}
+.progressbg {
+ stroke: rgb(231, 231, 232);
+ stroke-width: 8px;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-dasharray: 400;
+ stroke-dashoffset: 0;
+}
+.Cards {
+ fill: #f2f2f2;
+}
+.Symbol {
+ fill: url(#gradient);
+}
+.rtext {
+ font-size: 12px;
+ fill: #333;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+}
+.rtextnum {
+ font-size: 23px;
+ fill: #201e1e;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+.rtextmbms {
+ font-size: 12px;
+ fill: #5f5f5f;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+.jitter-Mob {
+ font-size: 9px;
+ fill: #5f5f5f;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+.startButton {
+ fill: url(#RadialGradient1);
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ -webkit-tap-highlight-color: transparent;
+ cursor: pointer;
+ pointer-events: visible;
+}
+.buttonTxt {
+ font-size: 40px;
+ fill: #ffffff;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+
+.intro-Progress {
+ stroke: #56c4fb;
+ stroke-width: 8px;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-dasharray: 400;
+ stroke-dashoffset: 0;
+}
+.progressElmstart {
+ stroke: #56c4fb;
+ stroke-width: 8px;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-dasharray: 400;
+ stroke-dashoffset: 0;
+ display: block;
+}
+.Startsettings {
+ fill: url(#RadialGradient1);
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ -webkit-tap-highlight-color: transparent;
+ cursor: pointer;
+ pointer-events: visible;
+ opacity: 0.1;
+ transition: opacity 1s ease-in-out;
+}
+.Startsettings:hover {
+ opacity: 1;
+}
+
+.progressbg {
+ stroke: rgb(231, 231, 232);
+ stroke-width: 8px;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-dasharray: 400;
+ stroke-dashoffset: 0;
+}
+.deskStart {
+ fill: none;
+ stroke: rgb(231, 231, 232);
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-width: 22px;
+ stroke-dasharray: 681;
+ stroke: url(#gradient);
+}
+#UI-Desk {
+ display: none;
+}
+#UI-Mob {
+ display: none;
+}
+.oDoTop-Speed {
+ font-size: 16.96px;
+ fill: gray;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: end;
+}
+#upSymbolDesk {
+ fill: #14b0fe;
+ display: none;
+}
+#downSymbolDesk {
+ fill: #14b0fe;
+ display: none;
+}
+#upSymbolMob {
+ fill: #14b0fe;
+ display: none;
+}
+#downSymbolMob {
+ fill: #14b0fe;
+ display: none;
+}
+
+#ipMob {
+ font-size: 15px;
+ fill: #201e1e;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+ display: none;
+}
+#ipDesk {
+ font-size: 15px;
+ fill: #201e1e;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+ display: none;
+}
+
+.spinner {
+ position: absolute;
+ z-index: 999;
+ top: 50vh;
+ left: 50vw;
+ text-align: center;
+}
+
+.spinner > div {
+ width: 20px;
+ height: 20px;
+ background-color: #2196f3;
+
+ border-radius: 100%;
+ display: inline-block;
+ -webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
+ animation: sk-bouncedelay 1.4s infinite ease-in-out both;
+}
+
+.spinner .bounce1 {
+ -webkit-animation-delay: -0.32s;
+ animation-delay: -0.32s;
+}
+
+.spinner .bounce2 {
+ -webkit-animation-delay: -0.16s;
+ animation-delay: -0.16s;
+}
+
+@-webkit-keyframes sk-bouncedelay {
+ 0%,
+ 80%,
+ 100% {
+ -webkit-transform: scale(0);
+ }
+ 40% {
+ -webkit-transform: scale(1);
+ }
+}
+
+@keyframes sk-bouncedelay {
+ 0%,
+ 80%,
+ 100% {
+ -webkit-transform: scale(0);
+ transform: scale(0);
+ }
+ 40% {
+ -webkit-transform: scale(1);
+ transform: scale(1);
+ }
+}
+
+.darkmode {
+ margin-bottom: -15px;
+ fill: #75757a99;
+ padding-top: 16px;
+ display: none;
+ cursor: pointer;
+ margin-right: 30px;
+}
+#daymode {
+ margin-right: 40px;
+}
+.darkmode:hover {
+ fill: #000000;
+}
+
+.Mobile,
+.Desktop {
+ visibility: hidden;
+ width: 100vw;
+ height: 100vh;
+}
+
+@media only screen and (orientation: landscape) {
+ .Mobile {
+ visibility: hidden;
+ }
+ .Desktop {
+ visibility: visible;
+ }
+}
+
+@media only screen and (orientation: portrait) {
+ .spinner {
+ top: 42vh;
+ left: 42vw;
+ }
+ .Mobile {
+ visibility: visible;
+ }
+ .Desktop {
+ visibility: hidden;
+ }
+}
+@media only screen and (max-width: 300px) {
+ .Credits{
+ display: none;
+ }
+}
diff --git a/data/agent/var/lib/openflare/pages/deployments/1/current/assets/css/darkmode.css b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/css/darkmode.css
new file mode 100644
index 00000000..14fef300
--- /dev/null
+++ b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/css/darkmode.css
@@ -0,0 +1,45 @@
+body {
+ background-color: #181818;
+}
+#ipDesk {
+ fill: aliceblue;
+}
+
+.oDoLive-Speed {
+ fill: #ffffff;
+}
+.oDoLive-Status {
+ fill: aliceblue;
+}
+#ipMob {
+ fill: aliceblue;
+}
+.rtextnum {
+ fill: #ffffff;
+}
+.rtextmbms {
+ fill: #ffffff;
+}
+.rtext {
+ fill: #ffffff;
+}
+.Cards {
+ fill: #000000;
+}
+
+.main-Gaugebg {
+ stroke: #000000;
+}
+.uiBg {
+ fill: #000000;
+}
+.progressbg {
+ stroke: #202020;
+}
+.jitter-Mob {
+ fill: #ffffff;
+}
+
+.ConnectError {
+ fill: #ffffff;
+}
diff --git a/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-500.eot b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-500.eot
new file mode 100644
index 00000000..fec03612
Binary files /dev/null and b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-500.eot differ
diff --git a/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-500.svg b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-500.svg
new file mode 100644
index 00000000..67eecf44
--- /dev/null
+++ b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-500.svg
@@ -0,0 +1,305 @@
+
+
+
diff --git a/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-500.ttf b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-500.ttf
new file mode 100644
index 00000000..4f515e2a
Binary files /dev/null and b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-500.ttf differ
diff --git a/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-500.woff b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-500.woff
new file mode 100644
index 00000000..d39bb52a
Binary files /dev/null and b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-500.woff differ
diff --git a/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-500.woff2 b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-500.woff2
new file mode 100644
index 00000000..29342a8d
Binary files /dev/null and b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-500.woff2 differ
diff --git a/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-regular.eot b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-regular.eot
new file mode 100644
index 00000000..c244b089
Binary files /dev/null and b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-regular.eot differ
diff --git a/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-regular.svg b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-regular.svg
new file mode 100644
index 00000000..627f5a36
--- /dev/null
+++ b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-regular.svg
@@ -0,0 +1,308 @@
+
+
+
diff --git a/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-regular.ttf b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-regular.ttf
new file mode 100644
index 00000000..d0e63254
Binary files /dev/null and b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-regular.ttf differ
diff --git a/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-regular.woff b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-regular.woff
new file mode 100644
index 00000000..9eaa94f9
Binary files /dev/null and b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-regular.woff differ
diff --git a/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-regular.woff2 b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-regular.woff2
new file mode 100644
index 00000000..020729ef
Binary files /dev/null and b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/fonts/roboto-v30-latin-regular.woff2 differ
diff --git a/data/agent/var/lib/openflare/pages/deployments/1/current/assets/images/app.svg b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/images/app.svg
new file mode 100644
index 00000000..01441ac0
--- /dev/null
+++ b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/images/app.svg
@@ -0,0 +1,459 @@
+
\ No newline at end of file
diff --git a/data/agent/var/lib/openflare/pages/deployments/1/current/assets/images/icons/android-chrome-192x192.png b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/images/icons/android-chrome-192x192.png
new file mode 100644
index 00000000..df78b83c
Binary files /dev/null and b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/images/icons/android-chrome-192x192.png differ
diff --git a/data/agent/var/lib/openflare/pages/deployments/1/current/assets/images/icons/android-chrome-512x512.png b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/images/icons/android-chrome-512x512.png
new file mode 100644
index 00000000..559d3eaf
Binary files /dev/null and b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/images/icons/android-chrome-512x512.png differ
diff --git a/data/agent/var/lib/openflare/pages/deployments/1/current/assets/images/icons/apple-touch-icon.png b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/images/icons/apple-touch-icon.png
new file mode 100644
index 00000000..14aec788
Binary files /dev/null and b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/images/icons/apple-touch-icon.png differ
diff --git a/data/agent/var/lib/openflare/pages/deployments/1/current/assets/images/icons/browserconfig.xml b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/images/icons/browserconfig.xml
new file mode 100644
index 00000000..ef1658c6
--- /dev/null
+++ b/data/agent/var/lib/openflare/pages/deployments/1/current/assets/images/icons/browserconfig.xml
@@ -0,0 +1,9 @@
+
+© Copyright 2013-2024 OpenSpeedTest™ All Rights Reserved.
+
+
+**No client-side software or plugin is required. You can run a network speed test from any device with a [Web Browser that is IE10 or newer.](https://www.youtube.com/watch?v=9f-OM_WQ7Bw&list=PLt-deStxFJOMEAs2O1lJhscMNzcg9E3Po&index=1)**
+
+
+
+
+## Why OpenSpeedTest
+
+
+
+### Secure by Design.
+
+
+
+OpenSpeedTest contains Only `STATIC` Files like `HTML`,`CSS` & `JS`.
+
+So you don't need to worry about Security Updates or Hidden Exploits that may compromise your secure environments.
+
+
+
+### Lightweight, High Performance.
+
+
+
+OpenSpeedTest is written in Vanilla JavaScript. No Third-Party frameworks or libraries were used. SpeedTest script file size is under 8kB gzip. The unexpected side effect of using Vanilla JavaScript is High Performance.
+
+
+
+### Run a speed test from Any Device.
+
+
+
+OpenSpeedTest will run on Any Web Browser that is IE10 or newer.
+
+
+
+### Ready for Any Display Size and Resolution.
+
+
+
+OpenSpeedTest User interface is written in SVG.
+
+
+
+# Create Your Own SpeedTest Server.
+
+### Server Requirements :
+
+`Nginx`, `Apache`, `IIS`, `Express`, or Any Web server that supports `HTTP/1.1` or newer.
+
+- Accept, `GET`, `POST`, `HEAD` & `OPTIONS`, Response `200 OK`.
+
+- Accept, `POST` to Static Files, Response `200 OK`.
+
+- `client_max_body_size`, 35 Megabytes or more.
+
+- Timeout greater than `60 seconds`.
+
+- Disable `Access logs` for Increasing server performance.
+
+- Improve `Time to First Byte` (TTFB)
+
+- Warning! If you run it behind a **[Reverse Proxy](https://github.com/openspeedtest/Speed-Test/issues/4#issuecomment-1229157193)**, you should increase the `post-body content length` to 35 megabytes.
+- Supports `HTTP2` & `HTTP3`.
+- `HTTP1.1` is recommended for achieving maximum performance.
+- **[You Should Follow our Nginx Config.](https://github.com/openspeedtest/Nginx-Configuration)**
+
+
+
+# Or, You can use OpenSpeedTest-Server.
+
+OpenSpeedTest-Server is available for Windows, Mac, Linux, Android, iOS & Docker.
+[](https://go.openspeedtest.com/Server "Download OpenSpeedTest-Server V2.1")
+#### Fully Optimized and ready to use applications.
+
+
+
+### New features:
+
+
+
+1. Stress Test. (Continuous Speed Test)
+
+
+
+To enable the stress test. Pass `Stress` or `S` keyword as a URL parameter.
+
+
+
+````
+
+http://192.168.1.5?Stress=Low
+
+````
+
+After the `STRESS` or `S` keyword, you can specify the number of seconds you need to run the StressTest in seconds, or preset values such as `Low`, `Medium`, `High`, `VeryHigh`, `Extreme`, `Day`, and `Year`. Will run a speed test for `300`,`600`,`900`,`1800`,`3600`,`86400`,`31557600` seconds, respectively. Also, you can feed the first letter of each parameter and its values.
+
+
+````
+
+http://192.168.1.5?S=L
+
+````
+
+`S=L` is the same as passing `Stress=low`
+
+Or you can specify the number of seconds eg:`5000` directly without any preset keywords.
+
+
+
+````
+
+http://192.168.1.5?Stress=5000
+
+````
+
+
+
+2. Run a speed test automatically
+
+
+
+Run a speed test automatically on page load.
+
+````
+
+http://192.168.1.5?Run
+
+````
+
+Run a speed test automatically after a few seconds.
+
+````
+
+http://192.168.1.5?Run=10 or http://192.168.1.5?R=10
+
+````
+
+
+
+You can pass multiple keywords, and it's not `Case-Sensitive`.
+
+
+
+````
+
+http://192.168.1.5?Run&Stress=300 OR http://192.168.1.5?R&S=300
+
+````
+
+This will start a speed test immediately and run for `300 seconds` in each direction. That is 300 seconds for download and `300 seconds` for upload.
+
+
+
+3. Save results to a Database
+
+
+
+Edit `Index.html`
+
+````
+
+var saveData = true;
+
+var saveDataURL = "//yourDatabase.Server.com:4500/save?data=";
+
+````
+
+4. Add multiple servers. The app will choose one with the least latency automatically.
+
+
+
+Edit `Index.html`
+
+````
+
+var openSpeedTestServerList = [
+
+{"ServerName":"Home-Earth", "Download":"/downloading", "Upload":"/upload", "ServerIcon":"DefaultIcon"},
+
+{"ServerName":"Home-Mars", "Download":"/downloading", "Upload":"/upload", "ServerIcon":"DefaultIcon"},
+
+{"ServerName":"Home-Moon", "Download":"/downloading", "Upload":"/upload", "ServerIcon":"DefaultIcon"}
+
+];
+
+````
+
+5. Disable or change Overhead Compensation factor.
+
+
+
+````
+
+http://192.168.1.5?clean
+
+````
+
+Overhead Compensation factor, This is browser based test, Many Unknowns. Currently 4%. That is within the margin of error.
+
+You can pass `Clean` or `C` as a URL Parameter and reset Overhead Compensation factor to Zero or set any value between 0 and 4. 1 = 1% to 4 = 4%.
+
+`Clean` will not accept values above 4, so Compensation is limited to maximum 4%.
+
+
+
+6. Change the default limit of 6 parallel HTTP connections to the Server.
+
+
+
+````
+
+http://192.168.1.5?XHR=3 OR http://192.168.1.5?X=3
+
+````
+
+Allow the user to Change the default limit of 6 parallel HTTP connections to the Server. `XHR` will Accept values above 1 and max 32
+
+pass `XHR` or `X` as a URL Parameter.
+
+
+
+7. Select a different server to run a speed test.
+
+
+
+````
+
+http://192.168.1.5?Host=http://192.168.55.1:90 OR http://192.168.1.5?h=http://192.168.55.1:90
+
+````
+
+Pass `Host` or `H` as a URL Parameter.
+
+`HOST` will Accept only valid HTTP URLs like `http://192.168.1.10:3000` or `https://yourHost.com`.
+
+
+
+8. Select and run one test at a time, `DOWNLOAD`, `UPLOAD`, or `PING`.
+
+
+
+````
+
+http://192.168.1.5?Test=Upload OR http://192.168.1.5?T=U
+
+````
+
+`TEST` Allow the user to select and run one test at a time, Download, Upload, or Ping.
+
+Pass `Test` or `T` as a URL Parameter.
+
+
+
+9. Set a PingTimeout dynamically by passing `Out` or `O` as a URL Parameter
+
+
+
+````
+
+http://192.168.1.5?Out=7000 OR http://192.168.1.5?O=7000
+
+````
+
+If Server not responded within 5 Seconds for any requests we send ('pingSamples' times)
+
+We will show `Network Error`, You can change the limit here.
+
+In milliseconds, if you need to set `6 seconds`. Change the value to `6000`.
+
+
+
+10. Set the Number of ping samples by adding `Ping` or `P` as a URL Parameter
+
+
+
+````
+
+http://192.168.1.5?Ping=500 OR http://192.168.1.5?P=500
+
+````
+
+More samples = more accurate representation. `Ping = 500` will send `501` requests to server to find the accurate ping value.
+Take a look at index.html, you can set a custom ping sample size, threads, upload data size etc.
+
+## Self-hosted (On-Premise) / (Docker Image/Source Code)
+### For Headless large-scale deployments.
+You have two options here. If you need a custom deployment, use our source code along with a web server of your choice. I prefer Nginx, and you can find my [Nginx Configuration](https://github.com/openspeedtest/Nginx-Configuration) here. Or you can use our docker image. You can deploy it on your LAN/WAN with or without an active internet connection.
+
+**This is docker implementation using nginxinc/nginx-unprivileged:stable-alpine. uses significantly fewer resources.**
+
+- NGINX Docker image that runs NGINX as a non root, unprivileged user.
+
+ ### Docker install instructions:
+
+ Install Docker and run the following command!
+
+````bash
+
+sudo docker run --restart=unless-stopped --name openspeedtest -d -p 3000:3000 -p 3001:3001 openspeedtest/latest
+
+````
+#### Or use docker-compose.yml
+````
+version: '3.3'
+services:
+ speedtest:
+ restart: unless-stopped
+ container_name: openspeedtest
+ ports:
+ - '3000:3000'
+ - '3001:3001'
+ image: openspeedtest/latest
+````
+- Warning! If you run it behind a **[Reverse Proxy](https://github.com/openspeedtest/Speed-Test/issues/4#issuecomment-1229157193)**, you should increase the `post-body content length` to 35 megabytes.
+
+- **[Follow our Nginx Config.](https://github.com/openspeedtest/Nginx-Configuration)**
+
+Now open your browser and direct it to:
+
+A: For **HTTP** use: `http://YOUR-SERVER-IP:3000`
+
+B: For **HTTPS** use: `https://YOUR-SERVER-IP:3001`
+
+#### Container-Port for http is 3000
+If you need to run this image on a different port for `HTTP`, Eg: change to `80` = `-p 80:3000`
+#### Container-Port for https is 3001
+If you need to run this image on a different port for `HTTPS`, Eg: change to `443` = `-p 443:3001`
+
+### Setup Free LetsEncrypt SSL with Automatic Certificate Renewal
+***Requirements***
+- PUBLIC IPV4 and/or IPV6 address.
+- A domain name that resolves to speed test server's IP address.
+- Email ID
+
+The following command will generate a Let's Encrypt certificate for your domain name and configure a cron job to automatically renew the certificate.
+
+````
+docker run -e ENABLE_LETSENCRYPT=True -e DOMAIN_NAME=speedtest.yourdomain.com -e USER_EMAIL=you@yourdomain.pro --restart=unless-stopped --name openspeedtest -d -p 80:3000 -p 443:3001 openspeedtest/latest
+````
+#### Or use docker-compose.yml
+````
+version: '3.3'
+services:
+ speedtest:
+ environment:
+ - ENABLE_LETSENCRYPT=True
+ - DOMAIN_NAME=speedtest.yourdomain.com
+ - USER_EMAIL=you@yourdomain.pro
+ restart: unless-stopped
+ container_name: openspeedtest
+ ports:
+ - '80:3000'
+ - '443:3001'
+ image: openspeedtest/latest
+````
+
+### How to Use Your Own Secure Sockets Layer (SSL) Certificate, Self-Signed or Paid?
+***Requirements***
+- Folder with your Certificate, Self-Signed or Paid.
+- Rename .cet file and .key file to `nginx.crt` & `nginx.key`
+
+ The folder needs to contain:
+
+- `nginx.crt`
+
+- `nginx.key`
+
+
+````
+sudo docker run --restart=unless-stopped --name openspeedtest -d -p 3000:3000 -p 3001:3001 openspeedtest/latest
+````
+
+To mount a folder with your own SSL certificate to this Docker container, append the following line to the above command:
+
+
+````bash
+
+-v /${PATH-TO-YOUR-OWN-SSL-CERTIFICATE}:/etc/ssl/
+
+````
+
+I am adding a folder with nginx.crt and nginx.key from my desktop by using the following command.
+
+````bash
+
+sudo docker run -v /Users/vishnu/Desktop/docker/:/etc/ssl/ --restart=unless-stopped --name openspeedtest -d -p 3000:3000 -p 3001:3001 openspeedtest/latest
+
+````
+#### Or use docker-compose.yml
+````
+version: '3.3'
+services:
+ speedtest:
+ volumes:
+ - '/Users/vishnu/Desktop/docker/:/etc/ssl/'
+ restart: unless-stopped
+ container_name: openspeedtest
+ ports:
+ - '3000:3000'
+ - '3001:3001'
+ image: openspeedtest/latest
+````
+## Advanced Configuration Options
+
+- Container Port Configuration
+
+To enable port changes, set the `CHANGE_CONTAINER_PORTS` environment variable to `"True"` and provide appropriate values for the following variables.
+
+`CHANGE_CONTAINER_PORTS=True`
+
+`HTTP_PORT=3000`
+
+`HTTPS_PORT=3001`
+
+- Set User
+
+`SET_USER=101`
+
+- Only Allow `CORS Request` from listed domains.
+
+`ALLOW_ONLY=domain1.com;domain2.com;domain3.com`
+
+- `SET_SERVER_NAME` Display the server name on the UI.
+
+`SET_SERVER_NAME=HOME-NAS`
+
+
+Docker images run better on Linux Platforms, including your NAS. But if you install docker on macOS or Windows, you may see poor performance. I asked this on Docker forums, and they told me macOS and Windows support is for Development purposes only. For Production, you need to use any Linux Platform.
+
+The same Story goes for Windows NGINX. Nginx uses only one worker even if you specify n number of worker processes. They will show in Task Manager, but the system will only use one. I got this information directly from the Nginx website.
+
+
+
+
+
+## Why do you need to Create Your Own SpeedTest Server?
+
+You can run OpenSpeedTest Server in your Home Lab, Office Server or Cloud Server. So that you or employees who work from home can run a speed test to your office and make sure they can run everything smoothly.
+
+
+
+**Choosing between ISP1 & ISP2.**
+
+
+
+Sometimes your ISP2 is Faster than ISP1 when you test your speed on popular speed test sites. But when you connect to your Home/Office/Cloud, that slower connection may perform better. The only way to find out is to run a speed test against your infrastructure.
+
+
+
+**Troubleshooting network issues.**
+
+
+
+It is common even when your Internet connection is working fine, but some of the devices in your network may experience trouble getting decent connectivity to the internet. The issue might be the wrong VLAN ID or Faulty Switch. If you run a Local network speed test, you can find and fix these issues easily.
+
+
+
+**Before you add a repeater.**
+
+
+
+Most repeaters will reduce your network speed by 50%, so if you put it far away, it will perform worst, and if you put it too close, you will not get enough coverage if you run a Local Network speed test. Depending on the application requirements, you can decide exactly where you need to put your repeater.
+
+
+
+**Browsing experience.**
+
+
+
+Many useful browser extensions are out there that we all know and love. But some of them are really slowing you down for **few seconds per page you visit**. You may see good performance when you test your network performance via File Transfer or Command-line utilities, but you may experience poor performance when browsing the internet. This is due to a bad browser configuration that including unwanted extensions installed. From my experience, only keep the one you are going to use every single day. Extension that you may use once in a while should be removed or disabled for maximum performance. If you see poor performance, try OpenSpeedTest from Private Window or Incognito Window. **This tool can be used to check the browser performance and impact of Extensions on your browsing experience.**
+
+
+
+**No client-side software or plugin is required. You can run a network speed test from any device with a [Web Browser that is IE10 or newer.](https://www.youtube.com/watch?v=9f-OM_WQ7Bw&list=PLt-deStxFJOMEAs2O1lJhscMNzcg9E3Po&index=1)**
+
+- Like this Project? Please **Donate NOW & Keep us Alive** -> https://go.openspeedtest.com/Donate
+
+MIT License
+
+Copyright (c) 2013 - 2023 OpenSpeedTest™
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/data/agent/var/lib/openflare/pages/deployments/1/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/css/app.css b/data/agent/var/lib/openflare/pages/deployments/1/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/css/app.css
new file mode 100644
index 00000000..39fa9393
--- /dev/null
+++ b/data/agent/var/lib/openflare/pages/deployments/1/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/css/app.css
@@ -0,0 +1,382 @@
+/* roboto-regular - latin */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+ src: url("..fonts/roboto-v30-latin-regular.eot"); /* IE9 Compat Modes */
+ src: local(""),
+ url("../fonts/roboto-v30-latin-regular.eot?#iefix")
+ format("embedded-opentype"),
+ /* IE6-IE8 */ url("../fonts/roboto-v30-latin-regular.woff2")
+ format("woff2"),
+ /* Super Modern Browsers */
+ url("../fonts/roboto-v30-latin-regular.woff") format("woff"),
+ /* Modern Browsers */ url("../fonts/roboto-v30-latin-regular.ttf")
+ format("truetype"),
+ /* Safari, Android, iOS */
+ url("../fonts/roboto-v30-latin-regular.svg#Roboto") format("svg"); /* Legacy iOS */
+}
+/* roboto-500 - latin */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 500;
+ font-display: swap;
+ src: url("assets/fonts/roboto-v30-latin-500.eot"); /* IE9 Compat Modes */
+ src: local(""),
+ url("assets/fonts/roboto-v30-latin-500.eot?#iefix")
+ format("embedded-opentype"),
+ /* IE6-IE8 */ url("../fonts/roboto-v30-latin-500.woff2")
+ format("woff2"),
+ /* Super Modern Browsers */ url("../fonts/roboto-v30-latin-500.woff")
+ format("woff"),
+ /* Modern Browsers */ url("../fonts/roboto-v30-latin-500.ttf")
+ format("truetype"),
+ /* Safari, Android, iOS */
+ url("../fonts/roboto-v30-latin-500.svg#Roboto") format("svg"); /* Legacy iOS */
+}
+
+body {
+ margin: 0px;
+ padding: 0px;
+ display: block;
+}
+
+::-webkit-scrollbar {
+ display: none;
+}
+html {
+ -ms-overflow-style: none;
+ scrollbar-width: none;
+}
+
+.Credits {
+color: rgb(125 119 119);
+text-align: center;
+font-size:14px;
+font-family: Roboto-Medium, Roboto;
+font-weight: 500;
+}
+.Credits a {
+ text-decoration: none;
+ color: rgb(113, 113, 113);
+}
+.Credits a:hover {
+color: #14b0fe;
+}
+
+.ConnectError {
+ display: none;
+
+}
+
+.openSpeedtestApp {
+
+ height: 100vh;
+ width: 100vw;
+ display: none;
+ overflow: hidden;
+}
+.main-Gaugebg {
+ fill: none;
+ stroke: rgb(231, 231, 232);
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-width: 22px;
+ stroke-dasharray: 681;
+}
+.main-GaugeBlue {
+ fill: none;
+ stroke: url(#gradient);
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-width: 22px;
+ stroke-dasharray: 681;
+ stroke-opacity: 0;
+}
+.main-GaugeWhite {
+ fill: none;
+ stroke: rgb(255, 255, 255);
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-width: 15px;
+ stroke-dasharray: 0, 681;
+ stroke-dashoffset: 1;
+ stroke-opacity: 0;
+}
+.oDo-Meter {
+ font-size: 16.633283615112305px;
+ fill: gray;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+}
+.oDoLive-Speed {
+ font-size: 28px;
+ fill: #201e1e;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+
+.oDoLive-Status {
+ font-size: 10px;
+ fill: #d2d1d2;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+.uiBg {
+ fill: #d2d1d2;
+}
+.progressbg {
+ stroke: rgb(231, 231, 232);
+ stroke-width: 8px;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-dasharray: 400;
+ stroke-dashoffset: 0;
+}
+.Cards {
+ fill: #f2f2f2;
+}
+.Symbol {
+ fill: url(#gradient);
+}
+.rtext {
+ font-size: 12px;
+ fill: #333;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+}
+.rtextnum {
+ font-size: 23px;
+ fill: #201e1e;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+.rtextmbms {
+ font-size: 12px;
+ fill: #5f5f5f;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+.jitter-Mob {
+ font-size: 9px;
+ fill: #5f5f5f;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+.startButton {
+ fill: url(#RadialGradient1);
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ -webkit-tap-highlight-color: transparent;
+ cursor: pointer;
+ pointer-events: visible;
+}
+.buttonTxt {
+ font-size: 40px;
+ fill: #ffffff;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+
+.intro-Progress {
+ stroke: #56c4fb;
+ stroke-width: 8px;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-dasharray: 400;
+ stroke-dashoffset: 0;
+}
+.progressElmstart {
+ stroke: #56c4fb;
+ stroke-width: 8px;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-dasharray: 400;
+ stroke-dashoffset: 0;
+ display: block;
+}
+.Startsettings {
+ fill: url(#RadialGradient1);
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ -webkit-tap-highlight-color: transparent;
+ cursor: pointer;
+ pointer-events: visible;
+ opacity: 0.1;
+ transition: opacity 1s ease-in-out;
+}
+.Startsettings:hover {
+ opacity: 1;
+}
+
+.progressbg {
+ stroke: rgb(231, 231, 232);
+ stroke-width: 8px;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-dasharray: 400;
+ stroke-dashoffset: 0;
+}
+.deskStart {
+ fill: none;
+ stroke: rgb(231, 231, 232);
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-width: 22px;
+ stroke-dasharray: 681;
+ stroke: url(#gradient);
+}
+#UI-Desk {
+ display: none;
+}
+#UI-Mob {
+ display: none;
+}
+.oDoTop-Speed {
+ font-size: 16.96px;
+ fill: gray;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: end;
+}
+#upSymbolDesk {
+ fill: #14b0fe;
+ display: none;
+}
+#downSymbolDesk {
+ fill: #14b0fe;
+ display: none;
+}
+#upSymbolMob {
+ fill: #14b0fe;
+ display: none;
+}
+#downSymbolMob {
+ fill: #14b0fe;
+ display: none;
+}
+
+#ipMob {
+ font-size: 15px;
+ fill: #201e1e;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+ display: none;
+}
+#ipDesk {
+ font-size: 15px;
+ fill: #201e1e;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+ display: none;
+}
+
+.spinner {
+ position: absolute;
+ z-index: 999;
+ top: 50vh;
+ left: 50vw;
+ text-align: center;
+}
+
+.spinner > div {
+ width: 20px;
+ height: 20px;
+ background-color: #2196f3;
+
+ border-radius: 100%;
+ display: inline-block;
+ -webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
+ animation: sk-bouncedelay 1.4s infinite ease-in-out both;
+}
+
+.spinner .bounce1 {
+ -webkit-animation-delay: -0.32s;
+ animation-delay: -0.32s;
+}
+
+.spinner .bounce2 {
+ -webkit-animation-delay: -0.16s;
+ animation-delay: -0.16s;
+}
+
+@-webkit-keyframes sk-bouncedelay {
+ 0%,
+ 80%,
+ 100% {
+ -webkit-transform: scale(0);
+ }
+ 40% {
+ -webkit-transform: scale(1);
+ }
+}
+
+@keyframes sk-bouncedelay {
+ 0%,
+ 80%,
+ 100% {
+ -webkit-transform: scale(0);
+ transform: scale(0);
+ }
+ 40% {
+ -webkit-transform: scale(1);
+ transform: scale(1);
+ }
+}
+
+.darkmode {
+ margin-bottom: -15px;
+ fill: #75757a99;
+ padding-top: 16px;
+ display: none;
+ cursor: pointer;
+ margin-right: 30px;
+}
+#daymode {
+ margin-right: 40px;
+}
+.darkmode:hover {
+ fill: #000000;
+}
+
+.Mobile,
+.Desktop {
+ visibility: hidden;
+ width: 100vw;
+ height: 100vh;
+}
+
+@media only screen and (orientation: landscape) {
+ .Mobile {
+ visibility: hidden;
+ }
+ .Desktop {
+ visibility: visible;
+ }
+}
+
+@media only screen and (orientation: portrait) {
+ .spinner {
+ top: 42vh;
+ left: 42vw;
+ }
+ .Mobile {
+ visibility: visible;
+ }
+ .Desktop {
+ visibility: hidden;
+ }
+}
+@media only screen and (max-width: 300px) {
+ .Credits{
+ display: none;
+ }
+}
diff --git a/data/agent/var/lib/openflare/pages/deployments/1/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/css/darkmode.css b/data/agent/var/lib/openflare/pages/deployments/1/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/css/darkmode.css
new file mode 100644
index 00000000..14fef300
--- /dev/null
+++ b/data/agent/var/lib/openflare/pages/deployments/1/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/css/darkmode.css
@@ -0,0 +1,45 @@
+body {
+ background-color: #181818;
+}
+#ipDesk {
+ fill: aliceblue;
+}
+
+.oDoLive-Speed {
+ fill: #ffffff;
+}
+.oDoLive-Status {
+ fill: aliceblue;
+}
+#ipMob {
+ fill: aliceblue;
+}
+.rtextnum {
+ fill: #ffffff;
+}
+.rtextmbms {
+ fill: #ffffff;
+}
+.rtext {
+ fill: #ffffff;
+}
+.Cards {
+ fill: #000000;
+}
+
+.main-Gaugebg {
+ stroke: #000000;
+}
+.uiBg {
+ fill: #000000;
+}
+.progressbg {
+ stroke: #202020;
+}
+.jitter-Mob {
+ fill: #ffffff;
+}
+
+.ConnectError {
+ fill: #ffffff;
+}
diff --git a/data/agent/var/lib/openflare/pages/deployments/1/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/fonts/roboto-v30-latin-500.eot b/data/agent/var/lib/openflare/pages/deployments/1/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/fonts/roboto-v30-latin-500.eot
new file mode 100644
index 00000000..fec03612
Binary files /dev/null and b/data/agent/var/lib/openflare/pages/deployments/1/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/fonts/roboto-v30-latin-500.eot differ
diff --git a/data/agent/var/lib/openflare/pages/deployments/1/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/fonts/roboto-v30-latin-500.svg b/data/agent/var/lib/openflare/pages/deployments/1/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/fonts/roboto-v30-latin-500.svg
new file mode 100644
index 00000000..67eecf44
--- /dev/null
+++ b/data/agent/var/lib/openflare/pages/deployments/1/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/fonts/roboto-v30-latin-500.svg
@@ -0,0 +1,305 @@
+
+
+© Copyright 2013-2024 OpenSpeedTest™ All Rights Reserved.
+
+
+**No client-side software or plugin is required. You can run a network speed test from any device with a [Web Browser that is IE10 or newer.](https://www.youtube.com/watch?v=9f-OM_WQ7Bw&list=PLt-deStxFJOMEAs2O1lJhscMNzcg9E3Po&index=1)**
+
+
+
+
+## Why OpenSpeedTest
+
+
+
+### Secure by Design.
+
+
+
+OpenSpeedTest contains Only `STATIC` Files like `HTML`,`CSS` & `JS`.
+
+So you don't need to worry about Security Updates or Hidden Exploits that may compromise your secure environments.
+
+
+
+### Lightweight, High Performance.
+
+
+
+OpenSpeedTest is written in Vanilla JavaScript. No Third-Party frameworks or libraries were used. SpeedTest script file size is under 8kB gzip. The unexpected side effect of using Vanilla JavaScript is High Performance.
+
+
+
+### Run a speed test from Any Device.
+
+
+
+OpenSpeedTest will run on Any Web Browser that is IE10 or newer.
+
+
+
+### Ready for Any Display Size and Resolution.
+
+
+
+OpenSpeedTest User interface is written in SVG.
+
+
+
+# Create Your Own SpeedTest Server.
+
+### Server Requirements :
+
+`Nginx`, `Apache`, `IIS`, `Express`, or Any Web server that supports `HTTP/1.1` or newer.
+
+- Accept, `GET`, `POST`, `HEAD` & `OPTIONS`, Response `200 OK`.
+
+- Accept, `POST` to Static Files, Response `200 OK`.
+
+- `client_max_body_size`, 35 Megabytes or more.
+
+- Timeout greater than `60 seconds`.
+
+- Disable `Access logs` for Increasing server performance.
+
+- Improve `Time to First Byte` (TTFB)
+
+- Warning! If you run it behind a **[Reverse Proxy](https://github.com/openspeedtest/Speed-Test/issues/4#issuecomment-1229157193)**, you should increase the `post-body content length` to 35 megabytes.
+- Supports `HTTP2` & `HTTP3`.
+- `HTTP1.1` is recommended for achieving maximum performance.
+- **[You Should Follow our Nginx Config.](https://github.com/openspeedtest/Nginx-Configuration)**
+
+
+
+# Or, You can use OpenSpeedTest-Server.
+
+OpenSpeedTest-Server is available for Windows, Mac, Linux, Android, iOS & Docker.
+[](https://go.openspeedtest.com/Server "Download OpenSpeedTest-Server V2.1")
+#### Fully Optimized and ready to use applications.
+
+
+
+### New features:
+
+
+
+1. Stress Test. (Continuous Speed Test)
+
+
+
+To enable the stress test. Pass `Stress` or `S` keyword as a URL parameter.
+
+
+
+````
+
+http://192.168.1.5?Stress=Low
+
+````
+
+After the `STRESS` or `S` keyword, you can specify the number of seconds you need to run the StressTest in seconds, or preset values such as `Low`, `Medium`, `High`, `VeryHigh`, `Extreme`, `Day`, and `Year`. Will run a speed test for `300`,`600`,`900`,`1800`,`3600`,`86400`,`31557600` seconds, respectively. Also, you can feed the first letter of each parameter and its values.
+
+
+````
+
+http://192.168.1.5?S=L
+
+````
+
+`S=L` is the same as passing `Stress=low`
+
+Or you can specify the number of seconds eg:`5000` directly without any preset keywords.
+
+
+
+````
+
+http://192.168.1.5?Stress=5000
+
+````
+
+
+
+2. Run a speed test automatically
+
+
+
+Run a speed test automatically on page load.
+
+````
+
+http://192.168.1.5?Run
+
+````
+
+Run a speed test automatically after a few seconds.
+
+````
+
+http://192.168.1.5?Run=10 or http://192.168.1.5?R=10
+
+````
+
+
+
+You can pass multiple keywords, and it's not `Case-Sensitive`.
+
+
+
+````
+
+http://192.168.1.5?Run&Stress=300 OR http://192.168.1.5?R&S=300
+
+````
+
+This will start a speed test immediately and run for `300 seconds` in each direction. That is 300 seconds for download and `300 seconds` for upload.
+
+
+
+3. Save results to a Database
+
+
+
+Edit `Index.html`
+
+````
+
+var saveData = true;
+
+var saveDataURL = "//yourDatabase.Server.com:4500/save?data=";
+
+````
+
+4. Add multiple servers. The app will choose one with the least latency automatically.
+
+
+
+Edit `Index.html`
+
+````
+
+var openSpeedTestServerList = [
+
+{"ServerName":"Home-Earth", "Download":"/downloading", "Upload":"/upload", "ServerIcon":"DefaultIcon"},
+
+{"ServerName":"Home-Mars", "Download":"/downloading", "Upload":"/upload", "ServerIcon":"DefaultIcon"},
+
+{"ServerName":"Home-Moon", "Download":"/downloading", "Upload":"/upload", "ServerIcon":"DefaultIcon"}
+
+];
+
+````
+
+5. Disable or change Overhead Compensation factor.
+
+
+
+````
+
+http://192.168.1.5?clean
+
+````
+
+Overhead Compensation factor, This is browser based test, Many Unknowns. Currently 4%. That is within the margin of error.
+
+You can pass `Clean` or `C` as a URL Parameter and reset Overhead Compensation factor to Zero or set any value between 0 and 4. 1 = 1% to 4 = 4%.
+
+`Clean` will not accept values above 4, so Compensation is limited to maximum 4%.
+
+
+
+6. Change the default limit of 6 parallel HTTP connections to the Server.
+
+
+
+````
+
+http://192.168.1.5?XHR=3 OR http://192.168.1.5?X=3
+
+````
+
+Allow the user to Change the default limit of 6 parallel HTTP connections to the Server. `XHR` will Accept values above 1 and max 32
+
+pass `XHR` or `X` as a URL Parameter.
+
+
+
+7. Select a different server to run a speed test.
+
+
+
+````
+
+http://192.168.1.5?Host=http://192.168.55.1:90 OR http://192.168.1.5?h=http://192.168.55.1:90
+
+````
+
+Pass `Host` or `H` as a URL Parameter.
+
+`HOST` will Accept only valid HTTP URLs like `http://192.168.1.10:3000` or `https://yourHost.com`.
+
+
+
+8. Select and run one test at a time, `DOWNLOAD`, `UPLOAD`, or `PING`.
+
+
+
+````
+
+http://192.168.1.5?Test=Upload OR http://192.168.1.5?T=U
+
+````
+
+`TEST` Allow the user to select and run one test at a time, Download, Upload, or Ping.
+
+Pass `Test` or `T` as a URL Parameter.
+
+
+
+9. Set a PingTimeout dynamically by passing `Out` or `O` as a URL Parameter
+
+
+
+````
+
+http://192.168.1.5?Out=7000 OR http://192.168.1.5?O=7000
+
+````
+
+If Server not responded within 5 Seconds for any requests we send ('pingSamples' times)
+
+We will show `Network Error`, You can change the limit here.
+
+In milliseconds, if you need to set `6 seconds`. Change the value to `6000`.
+
+
+
+10. Set the Number of ping samples by adding `Ping` or `P` as a URL Parameter
+
+
+
+````
+
+http://192.168.1.5?Ping=500 OR http://192.168.1.5?P=500
+
+````
+
+More samples = more accurate representation. `Ping = 500` will send `501` requests to server to find the accurate ping value.
+Take a look at index.html, you can set a custom ping sample size, threads, upload data size etc.
+
+## Self-hosted (On-Premise) / (Docker Image/Source Code)
+### For Headless large-scale deployments.
+You have two options here. If you need a custom deployment, use our source code along with a web server of your choice. I prefer Nginx, and you can find my [Nginx Configuration](https://github.com/openspeedtest/Nginx-Configuration) here. Or you can use our docker image. You can deploy it on your LAN/WAN with or without an active internet connection.
+
+**This is docker implementation using nginxinc/nginx-unprivileged:stable-alpine. uses significantly fewer resources.**
+
+- NGINX Docker image that runs NGINX as a non root, unprivileged user.
+
+ ### Docker install instructions:
+
+ Install Docker and run the following command!
+
+````bash
+
+sudo docker run --restart=unless-stopped --name openspeedtest -d -p 3000:3000 -p 3001:3001 openspeedtest/latest
+
+````
+#### Or use docker-compose.yml
+````
+version: '3.3'
+services:
+ speedtest:
+ restart: unless-stopped
+ container_name: openspeedtest
+ ports:
+ - '3000:3000'
+ - '3001:3001'
+ image: openspeedtest/latest
+````
+- Warning! If you run it behind a **[Reverse Proxy](https://github.com/openspeedtest/Speed-Test/issues/4#issuecomment-1229157193)**, you should increase the `post-body content length` to 35 megabytes.
+
+- **[Follow our Nginx Config.](https://github.com/openspeedtest/Nginx-Configuration)**
+
+Now open your browser and direct it to:
+
+A: For **HTTP** use: `http://YOUR-SERVER-IP:3000`
+
+B: For **HTTPS** use: `https://YOUR-SERVER-IP:3001`
+
+#### Container-Port for http is 3000
+If you need to run this image on a different port for `HTTP`, Eg: change to `80` = `-p 80:3000`
+#### Container-Port for https is 3001
+If you need to run this image on a different port for `HTTPS`, Eg: change to `443` = `-p 443:3001`
+
+### Setup Free LetsEncrypt SSL with Automatic Certificate Renewal
+***Requirements***
+- PUBLIC IPV4 and/or IPV6 address.
+- A domain name that resolves to speed test server's IP address.
+- Email ID
+
+The following command will generate a Let's Encrypt certificate for your domain name and configure a cron job to automatically renew the certificate.
+
+````
+docker run -e ENABLE_LETSENCRYPT=True -e DOMAIN_NAME=speedtest.yourdomain.com -e USER_EMAIL=you@yourdomain.pro --restart=unless-stopped --name openspeedtest -d -p 80:3000 -p 443:3001 openspeedtest/latest
+````
+#### Or use docker-compose.yml
+````
+version: '3.3'
+services:
+ speedtest:
+ environment:
+ - ENABLE_LETSENCRYPT=True
+ - DOMAIN_NAME=speedtest.yourdomain.com
+ - USER_EMAIL=you@yourdomain.pro
+ restart: unless-stopped
+ container_name: openspeedtest
+ ports:
+ - '80:3000'
+ - '443:3001'
+ image: openspeedtest/latest
+````
+
+### How to Use Your Own Secure Sockets Layer (SSL) Certificate, Self-Signed or Paid?
+***Requirements***
+- Folder with your Certificate, Self-Signed or Paid.
+- Rename .cet file and .key file to `nginx.crt` & `nginx.key`
+
+ The folder needs to contain:
+
+- `nginx.crt`
+
+- `nginx.key`
+
+
+````
+sudo docker run --restart=unless-stopped --name openspeedtest -d -p 3000:3000 -p 3001:3001 openspeedtest/latest
+````
+
+To mount a folder with your own SSL certificate to this Docker container, append the following line to the above command:
+
+
+````bash
+
+-v /${PATH-TO-YOUR-OWN-SSL-CERTIFICATE}:/etc/ssl/
+
+````
+
+I am adding a folder with nginx.crt and nginx.key from my desktop by using the following command.
+
+````bash
+
+sudo docker run -v /Users/vishnu/Desktop/docker/:/etc/ssl/ --restart=unless-stopped --name openspeedtest -d -p 3000:3000 -p 3001:3001 openspeedtest/latest
+
+````
+#### Or use docker-compose.yml
+````
+version: '3.3'
+services:
+ speedtest:
+ volumes:
+ - '/Users/vishnu/Desktop/docker/:/etc/ssl/'
+ restart: unless-stopped
+ container_name: openspeedtest
+ ports:
+ - '3000:3000'
+ - '3001:3001'
+ image: openspeedtest/latest
+````
+## Advanced Configuration Options
+
+- Container Port Configuration
+
+To enable port changes, set the `CHANGE_CONTAINER_PORTS` environment variable to `"True"` and provide appropriate values for the following variables.
+
+`CHANGE_CONTAINER_PORTS=True`
+
+`HTTP_PORT=3000`
+
+`HTTPS_PORT=3001`
+
+- Set User
+
+`SET_USER=101`
+
+- Only Allow `CORS Request` from listed domains.
+
+`ALLOW_ONLY=domain1.com;domain2.com;domain3.com`
+
+- `SET_SERVER_NAME` Display the server name on the UI.
+
+`SET_SERVER_NAME=HOME-NAS`
+
+
+Docker images run better on Linux Platforms, including your NAS. But if you install docker on macOS or Windows, you may see poor performance. I asked this on Docker forums, and they told me macOS and Windows support is for Development purposes only. For Production, you need to use any Linux Platform.
+
+The same Story goes for Windows NGINX. Nginx uses only one worker even if you specify n number of worker processes. They will show in Task Manager, but the system will only use one. I got this information directly from the Nginx website.
+
+
+
+
+
+## Why do you need to Create Your Own SpeedTest Server?
+
+You can run OpenSpeedTest Server in your Home Lab, Office Server or Cloud Server. So that you or employees who work from home can run a speed test to your office and make sure they can run everything smoothly.
+
+
+
+**Choosing between ISP1 & ISP2.**
+
+
+
+Sometimes your ISP2 is Faster than ISP1 when you test your speed on popular speed test sites. But when you connect to your Home/Office/Cloud, that slower connection may perform better. The only way to find out is to run a speed test against your infrastructure.
+
+
+
+**Troubleshooting network issues.**
+
+
+
+It is common even when your Internet connection is working fine, but some of the devices in your network may experience trouble getting decent connectivity to the internet. The issue might be the wrong VLAN ID or Faulty Switch. If you run a Local network speed test, you can find and fix these issues easily.
+
+
+
+**Before you add a repeater.**
+
+
+
+Most repeaters will reduce your network speed by 50%, so if you put it far away, it will perform worst, and if you put it too close, you will not get enough coverage if you run a Local Network speed test. Depending on the application requirements, you can decide exactly where you need to put your repeater.
+
+
+
+**Browsing experience.**
+
+
+
+Many useful browser extensions are out there that we all know and love. But some of them are really slowing you down for **few seconds per page you visit**. You may see good performance when you test your network performance via File Transfer or Command-line utilities, but you may experience poor performance when browsing the internet. This is due to a bad browser configuration that including unwanted extensions installed. From my experience, only keep the one you are going to use every single day. Extension that you may use once in a while should be removed or disabled for maximum performance. If you see poor performance, try OpenSpeedTest from Private Window or Incognito Window. **This tool can be used to check the browser performance and impact of Extensions on your browsing experience.**
+
+
+
+**No client-side software or plugin is required. You can run a network speed test from any device with a [Web Browser that is IE10 or newer.](https://www.youtube.com/watch?v=9f-OM_WQ7Bw&list=PLt-deStxFJOMEAs2O1lJhscMNzcg9E3Po&index=1)**
+
+- Like this Project? Please **Donate NOW & Keep us Alive** -> https://go.openspeedtest.com/Donate
+
+MIT License
+
+Copyright (c) 2013 - 2023 OpenSpeedTest™
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/data/agent/var/lib/openflare/pages/deployments/3/current/assets/css/app.css b/data/agent/var/lib/openflare/pages/deployments/3/current/assets/css/app.css
new file mode 100644
index 00000000..39fa9393
--- /dev/null
+++ b/data/agent/var/lib/openflare/pages/deployments/3/current/assets/css/app.css
@@ -0,0 +1,382 @@
+/* roboto-regular - latin */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+ src: url("..fonts/roboto-v30-latin-regular.eot"); /* IE9 Compat Modes */
+ src: local(""),
+ url("../fonts/roboto-v30-latin-regular.eot?#iefix")
+ format("embedded-opentype"),
+ /* IE6-IE8 */ url("../fonts/roboto-v30-latin-regular.woff2")
+ format("woff2"),
+ /* Super Modern Browsers */
+ url("../fonts/roboto-v30-latin-regular.woff") format("woff"),
+ /* Modern Browsers */ url("../fonts/roboto-v30-latin-regular.ttf")
+ format("truetype"),
+ /* Safari, Android, iOS */
+ url("../fonts/roboto-v30-latin-regular.svg#Roboto") format("svg"); /* Legacy iOS */
+}
+/* roboto-500 - latin */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 500;
+ font-display: swap;
+ src: url("assets/fonts/roboto-v30-latin-500.eot"); /* IE9 Compat Modes */
+ src: local(""),
+ url("assets/fonts/roboto-v30-latin-500.eot?#iefix")
+ format("embedded-opentype"),
+ /* IE6-IE8 */ url("../fonts/roboto-v30-latin-500.woff2")
+ format("woff2"),
+ /* Super Modern Browsers */ url("../fonts/roboto-v30-latin-500.woff")
+ format("woff"),
+ /* Modern Browsers */ url("../fonts/roboto-v30-latin-500.ttf")
+ format("truetype"),
+ /* Safari, Android, iOS */
+ url("../fonts/roboto-v30-latin-500.svg#Roboto") format("svg"); /* Legacy iOS */
+}
+
+body {
+ margin: 0px;
+ padding: 0px;
+ display: block;
+}
+
+::-webkit-scrollbar {
+ display: none;
+}
+html {
+ -ms-overflow-style: none;
+ scrollbar-width: none;
+}
+
+.Credits {
+color: rgb(125 119 119);
+text-align: center;
+font-size:14px;
+font-family: Roboto-Medium, Roboto;
+font-weight: 500;
+}
+.Credits a {
+ text-decoration: none;
+ color: rgb(113, 113, 113);
+}
+.Credits a:hover {
+color: #14b0fe;
+}
+
+.ConnectError {
+ display: none;
+
+}
+
+.openSpeedtestApp {
+
+ height: 100vh;
+ width: 100vw;
+ display: none;
+ overflow: hidden;
+}
+.main-Gaugebg {
+ fill: none;
+ stroke: rgb(231, 231, 232);
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-width: 22px;
+ stroke-dasharray: 681;
+}
+.main-GaugeBlue {
+ fill: none;
+ stroke: url(#gradient);
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-width: 22px;
+ stroke-dasharray: 681;
+ stroke-opacity: 0;
+}
+.main-GaugeWhite {
+ fill: none;
+ stroke: rgb(255, 255, 255);
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-width: 15px;
+ stroke-dasharray: 0, 681;
+ stroke-dashoffset: 1;
+ stroke-opacity: 0;
+}
+.oDo-Meter {
+ font-size: 16.633283615112305px;
+ fill: gray;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+}
+.oDoLive-Speed {
+ font-size: 28px;
+ fill: #201e1e;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+
+.oDoLive-Status {
+ font-size: 10px;
+ fill: #d2d1d2;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+.uiBg {
+ fill: #d2d1d2;
+}
+.progressbg {
+ stroke: rgb(231, 231, 232);
+ stroke-width: 8px;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-dasharray: 400;
+ stroke-dashoffset: 0;
+}
+.Cards {
+ fill: #f2f2f2;
+}
+.Symbol {
+ fill: url(#gradient);
+}
+.rtext {
+ font-size: 12px;
+ fill: #333;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+}
+.rtextnum {
+ font-size: 23px;
+ fill: #201e1e;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+.rtextmbms {
+ font-size: 12px;
+ fill: #5f5f5f;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+.jitter-Mob {
+ font-size: 9px;
+ fill: #5f5f5f;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+.startButton {
+ fill: url(#RadialGradient1);
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ -webkit-tap-highlight-color: transparent;
+ cursor: pointer;
+ pointer-events: visible;
+}
+.buttonTxt {
+ font-size: 40px;
+ fill: #ffffff;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+
+.intro-Progress {
+ stroke: #56c4fb;
+ stroke-width: 8px;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-dasharray: 400;
+ stroke-dashoffset: 0;
+}
+.progressElmstart {
+ stroke: #56c4fb;
+ stroke-width: 8px;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-dasharray: 400;
+ stroke-dashoffset: 0;
+ display: block;
+}
+.Startsettings {
+ fill: url(#RadialGradient1);
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ -webkit-tap-highlight-color: transparent;
+ cursor: pointer;
+ pointer-events: visible;
+ opacity: 0.1;
+ transition: opacity 1s ease-in-out;
+}
+.Startsettings:hover {
+ opacity: 1;
+}
+
+.progressbg {
+ stroke: rgb(231, 231, 232);
+ stroke-width: 8px;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-dasharray: 400;
+ stroke-dashoffset: 0;
+}
+.deskStart {
+ fill: none;
+ stroke: rgb(231, 231, 232);
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-width: 22px;
+ stroke-dasharray: 681;
+ stroke: url(#gradient);
+}
+#UI-Desk {
+ display: none;
+}
+#UI-Mob {
+ display: none;
+}
+.oDoTop-Speed {
+ font-size: 16.96px;
+ fill: gray;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: end;
+}
+#upSymbolDesk {
+ fill: #14b0fe;
+ display: none;
+}
+#downSymbolDesk {
+ fill: #14b0fe;
+ display: none;
+}
+#upSymbolMob {
+ fill: #14b0fe;
+ display: none;
+}
+#downSymbolMob {
+ fill: #14b0fe;
+ display: none;
+}
+
+#ipMob {
+ font-size: 15px;
+ fill: #201e1e;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+ display: none;
+}
+#ipDesk {
+ font-size: 15px;
+ fill: #201e1e;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+ display: none;
+}
+
+.spinner {
+ position: absolute;
+ z-index: 999;
+ top: 50vh;
+ left: 50vw;
+ text-align: center;
+}
+
+.spinner > div {
+ width: 20px;
+ height: 20px;
+ background-color: #2196f3;
+
+ border-radius: 100%;
+ display: inline-block;
+ -webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
+ animation: sk-bouncedelay 1.4s infinite ease-in-out both;
+}
+
+.spinner .bounce1 {
+ -webkit-animation-delay: -0.32s;
+ animation-delay: -0.32s;
+}
+
+.spinner .bounce2 {
+ -webkit-animation-delay: -0.16s;
+ animation-delay: -0.16s;
+}
+
+@-webkit-keyframes sk-bouncedelay {
+ 0%,
+ 80%,
+ 100% {
+ -webkit-transform: scale(0);
+ }
+ 40% {
+ -webkit-transform: scale(1);
+ }
+}
+
+@keyframes sk-bouncedelay {
+ 0%,
+ 80%,
+ 100% {
+ -webkit-transform: scale(0);
+ transform: scale(0);
+ }
+ 40% {
+ -webkit-transform: scale(1);
+ transform: scale(1);
+ }
+}
+
+.darkmode {
+ margin-bottom: -15px;
+ fill: #75757a99;
+ padding-top: 16px;
+ display: none;
+ cursor: pointer;
+ margin-right: 30px;
+}
+#daymode {
+ margin-right: 40px;
+}
+.darkmode:hover {
+ fill: #000000;
+}
+
+.Mobile,
+.Desktop {
+ visibility: hidden;
+ width: 100vw;
+ height: 100vh;
+}
+
+@media only screen and (orientation: landscape) {
+ .Mobile {
+ visibility: hidden;
+ }
+ .Desktop {
+ visibility: visible;
+ }
+}
+
+@media only screen and (orientation: portrait) {
+ .spinner {
+ top: 42vh;
+ left: 42vw;
+ }
+ .Mobile {
+ visibility: visible;
+ }
+ .Desktop {
+ visibility: hidden;
+ }
+}
+@media only screen and (max-width: 300px) {
+ .Credits{
+ display: none;
+ }
+}
diff --git a/data/agent/var/lib/openflare/pages/deployments/3/current/assets/css/darkmode.css b/data/agent/var/lib/openflare/pages/deployments/3/current/assets/css/darkmode.css
new file mode 100644
index 00000000..14fef300
--- /dev/null
+++ b/data/agent/var/lib/openflare/pages/deployments/3/current/assets/css/darkmode.css
@@ -0,0 +1,45 @@
+body {
+ background-color: #181818;
+}
+#ipDesk {
+ fill: aliceblue;
+}
+
+.oDoLive-Speed {
+ fill: #ffffff;
+}
+.oDoLive-Status {
+ fill: aliceblue;
+}
+#ipMob {
+ fill: aliceblue;
+}
+.rtextnum {
+ fill: #ffffff;
+}
+.rtextmbms {
+ fill: #ffffff;
+}
+.rtext {
+ fill: #ffffff;
+}
+.Cards {
+ fill: #000000;
+}
+
+.main-Gaugebg {
+ stroke: #000000;
+}
+.uiBg {
+ fill: #000000;
+}
+.progressbg {
+ stroke: #202020;
+}
+.jitter-Mob {
+ fill: #ffffff;
+}
+
+.ConnectError {
+ fill: #ffffff;
+}
diff --git a/data/agent/var/lib/openflare/pages/deployments/3/current/assets/fonts/roboto-v30-latin-500.eot b/data/agent/var/lib/openflare/pages/deployments/3/current/assets/fonts/roboto-v30-latin-500.eot
new file mode 100644
index 00000000..fec03612
Binary files /dev/null and b/data/agent/var/lib/openflare/pages/deployments/3/current/assets/fonts/roboto-v30-latin-500.eot differ
diff --git a/data/agent/var/lib/openflare/pages/deployments/3/current/assets/fonts/roboto-v30-latin-500.svg b/data/agent/var/lib/openflare/pages/deployments/3/current/assets/fonts/roboto-v30-latin-500.svg
new file mode 100644
index 00000000..67eecf44
--- /dev/null
+++ b/data/agent/var/lib/openflare/pages/deployments/3/current/assets/fonts/roboto-v30-latin-500.svg
@@ -0,0 +1,305 @@
+
+
+© Copyright 2013-2024 OpenSpeedTest™ All Rights Reserved.
+
+
+**No client-side software or plugin is required. You can run a network speed test from any device with a [Web Browser that is IE10 or newer.](https://www.youtube.com/watch?v=9f-OM_WQ7Bw&list=PLt-deStxFJOMEAs2O1lJhscMNzcg9E3Po&index=1)**
+
+
+
+
+## Why OpenSpeedTest
+
+
+
+### Secure by Design.
+
+
+
+OpenSpeedTest contains Only `STATIC` Files like `HTML`,`CSS` & `JS`.
+
+So you don't need to worry about Security Updates or Hidden Exploits that may compromise your secure environments.
+
+
+
+### Lightweight, High Performance.
+
+
+
+OpenSpeedTest is written in Vanilla JavaScript. No Third-Party frameworks or libraries were used. SpeedTest script file size is under 8kB gzip. The unexpected side effect of using Vanilla JavaScript is High Performance.
+
+
+
+### Run a speed test from Any Device.
+
+
+
+OpenSpeedTest will run on Any Web Browser that is IE10 or newer.
+
+
+
+### Ready for Any Display Size and Resolution.
+
+
+
+OpenSpeedTest User interface is written in SVG.
+
+
+
+# Create Your Own SpeedTest Server.
+
+### Server Requirements :
+
+`Nginx`, `Apache`, `IIS`, `Express`, or Any Web server that supports `HTTP/1.1` or newer.
+
+- Accept, `GET`, `POST`, `HEAD` & `OPTIONS`, Response `200 OK`.
+
+- Accept, `POST` to Static Files, Response `200 OK`.
+
+- `client_max_body_size`, 35 Megabytes or more.
+
+- Timeout greater than `60 seconds`.
+
+- Disable `Access logs` for Increasing server performance.
+
+- Improve `Time to First Byte` (TTFB)
+
+- Warning! If you run it behind a **[Reverse Proxy](https://github.com/openspeedtest/Speed-Test/issues/4#issuecomment-1229157193)**, you should increase the `post-body content length` to 35 megabytes.
+- Supports `HTTP2` & `HTTP3`.
+- `HTTP1.1` is recommended for achieving maximum performance.
+- **[You Should Follow our Nginx Config.](https://github.com/openspeedtest/Nginx-Configuration)**
+
+
+
+# Or, You can use OpenSpeedTest-Server.
+
+OpenSpeedTest-Server is available for Windows, Mac, Linux, Android, iOS & Docker.
+[](https://go.openspeedtest.com/Server "Download OpenSpeedTest-Server V2.1")
+#### Fully Optimized and ready to use applications.
+
+
+
+### New features:
+
+
+
+1. Stress Test. (Continuous Speed Test)
+
+
+
+To enable the stress test. Pass `Stress` or `S` keyword as a URL parameter.
+
+
+
+````
+
+http://192.168.1.5?Stress=Low
+
+````
+
+After the `STRESS` or `S` keyword, you can specify the number of seconds you need to run the StressTest in seconds, or preset values such as `Low`, `Medium`, `High`, `VeryHigh`, `Extreme`, `Day`, and `Year`. Will run a speed test for `300`,`600`,`900`,`1800`,`3600`,`86400`,`31557600` seconds, respectively. Also, you can feed the first letter of each parameter and its values.
+
+
+````
+
+http://192.168.1.5?S=L
+
+````
+
+`S=L` is the same as passing `Stress=low`
+
+Or you can specify the number of seconds eg:`5000` directly without any preset keywords.
+
+
+
+````
+
+http://192.168.1.5?Stress=5000
+
+````
+
+
+
+2. Run a speed test automatically
+
+
+
+Run a speed test automatically on page load.
+
+````
+
+http://192.168.1.5?Run
+
+````
+
+Run a speed test automatically after a few seconds.
+
+````
+
+http://192.168.1.5?Run=10 or http://192.168.1.5?R=10
+
+````
+
+
+
+You can pass multiple keywords, and it's not `Case-Sensitive`.
+
+
+
+````
+
+http://192.168.1.5?Run&Stress=300 OR http://192.168.1.5?R&S=300
+
+````
+
+This will start a speed test immediately and run for `300 seconds` in each direction. That is 300 seconds for download and `300 seconds` for upload.
+
+
+
+3. Save results to a Database
+
+
+
+Edit `Index.html`
+
+````
+
+var saveData = true;
+
+var saveDataURL = "//yourDatabase.Server.com:4500/save?data=";
+
+````
+
+4. Add multiple servers. The app will choose one with the least latency automatically.
+
+
+
+Edit `Index.html`
+
+````
+
+var openSpeedTestServerList = [
+
+{"ServerName":"Home-Earth", "Download":"/downloading", "Upload":"/upload", "ServerIcon":"DefaultIcon"},
+
+{"ServerName":"Home-Mars", "Download":"/downloading", "Upload":"/upload", "ServerIcon":"DefaultIcon"},
+
+{"ServerName":"Home-Moon", "Download":"/downloading", "Upload":"/upload", "ServerIcon":"DefaultIcon"}
+
+];
+
+````
+
+5. Disable or change Overhead Compensation factor.
+
+
+
+````
+
+http://192.168.1.5?clean
+
+````
+
+Overhead Compensation factor, This is browser based test, Many Unknowns. Currently 4%. That is within the margin of error.
+
+You can pass `Clean` or `C` as a URL Parameter and reset Overhead Compensation factor to Zero or set any value between 0 and 4. 1 = 1% to 4 = 4%.
+
+`Clean` will not accept values above 4, so Compensation is limited to maximum 4%.
+
+
+
+6. Change the default limit of 6 parallel HTTP connections to the Server.
+
+
+
+````
+
+http://192.168.1.5?XHR=3 OR http://192.168.1.5?X=3
+
+````
+
+Allow the user to Change the default limit of 6 parallel HTTP connections to the Server. `XHR` will Accept values above 1 and max 32
+
+pass `XHR` or `X` as a URL Parameter.
+
+
+
+7. Select a different server to run a speed test.
+
+
+
+````
+
+http://192.168.1.5?Host=http://192.168.55.1:90 OR http://192.168.1.5?h=http://192.168.55.1:90
+
+````
+
+Pass `Host` or `H` as a URL Parameter.
+
+`HOST` will Accept only valid HTTP URLs like `http://192.168.1.10:3000` or `https://yourHost.com`.
+
+
+
+8. Select and run one test at a time, `DOWNLOAD`, `UPLOAD`, or `PING`.
+
+
+
+````
+
+http://192.168.1.5?Test=Upload OR http://192.168.1.5?T=U
+
+````
+
+`TEST` Allow the user to select and run one test at a time, Download, Upload, or Ping.
+
+Pass `Test` or `T` as a URL Parameter.
+
+
+
+9. Set a PingTimeout dynamically by passing `Out` or `O` as a URL Parameter
+
+
+
+````
+
+http://192.168.1.5?Out=7000 OR http://192.168.1.5?O=7000
+
+````
+
+If Server not responded within 5 Seconds for any requests we send ('pingSamples' times)
+
+We will show `Network Error`, You can change the limit here.
+
+In milliseconds, if you need to set `6 seconds`. Change the value to `6000`.
+
+
+
+10. Set the Number of ping samples by adding `Ping` or `P` as a URL Parameter
+
+
+
+````
+
+http://192.168.1.5?Ping=500 OR http://192.168.1.5?P=500
+
+````
+
+More samples = more accurate representation. `Ping = 500` will send `501` requests to server to find the accurate ping value.
+Take a look at index.html, you can set a custom ping sample size, threads, upload data size etc.
+
+## Self-hosted (On-Premise) / (Docker Image/Source Code)
+### For Headless large-scale deployments.
+You have two options here. If you need a custom deployment, use our source code along with a web server of your choice. I prefer Nginx, and you can find my [Nginx Configuration](https://github.com/openspeedtest/Nginx-Configuration) here. Or you can use our docker image. You can deploy it on your LAN/WAN with or without an active internet connection.
+
+**This is docker implementation using nginxinc/nginx-unprivileged:stable-alpine. uses significantly fewer resources.**
+
+- NGINX Docker image that runs NGINX as a non root, unprivileged user.
+
+ ### Docker install instructions:
+
+ Install Docker and run the following command!
+
+````bash
+
+sudo docker run --restart=unless-stopped --name openspeedtest -d -p 3000:3000 -p 3001:3001 openspeedtest/latest
+
+````
+#### Or use docker-compose.yml
+````
+version: '3.3'
+services:
+ speedtest:
+ restart: unless-stopped
+ container_name: openspeedtest
+ ports:
+ - '3000:3000'
+ - '3001:3001'
+ image: openspeedtest/latest
+````
+- Warning! If you run it behind a **[Reverse Proxy](https://github.com/openspeedtest/Speed-Test/issues/4#issuecomment-1229157193)**, you should increase the `post-body content length` to 35 megabytes.
+
+- **[Follow our Nginx Config.](https://github.com/openspeedtest/Nginx-Configuration)**
+
+Now open your browser and direct it to:
+
+A: For **HTTP** use: `http://YOUR-SERVER-IP:3000`
+
+B: For **HTTPS** use: `https://YOUR-SERVER-IP:3001`
+
+#### Container-Port for http is 3000
+If you need to run this image on a different port for `HTTP`, Eg: change to `80` = `-p 80:3000`
+#### Container-Port for https is 3001
+If you need to run this image on a different port for `HTTPS`, Eg: change to `443` = `-p 443:3001`
+
+### Setup Free LetsEncrypt SSL with Automatic Certificate Renewal
+***Requirements***
+- PUBLIC IPV4 and/or IPV6 address.
+- A domain name that resolves to speed test server's IP address.
+- Email ID
+
+The following command will generate a Let's Encrypt certificate for your domain name and configure a cron job to automatically renew the certificate.
+
+````
+docker run -e ENABLE_LETSENCRYPT=True -e DOMAIN_NAME=speedtest.yourdomain.com -e USER_EMAIL=you@yourdomain.pro --restart=unless-stopped --name openspeedtest -d -p 80:3000 -p 443:3001 openspeedtest/latest
+````
+#### Or use docker-compose.yml
+````
+version: '3.3'
+services:
+ speedtest:
+ environment:
+ - ENABLE_LETSENCRYPT=True
+ - DOMAIN_NAME=speedtest.yourdomain.com
+ - USER_EMAIL=you@yourdomain.pro
+ restart: unless-stopped
+ container_name: openspeedtest
+ ports:
+ - '80:3000'
+ - '443:3001'
+ image: openspeedtest/latest
+````
+
+### How to Use Your Own Secure Sockets Layer (SSL) Certificate, Self-Signed or Paid?
+***Requirements***
+- Folder with your Certificate, Self-Signed or Paid.
+- Rename .cet file and .key file to `nginx.crt` & `nginx.key`
+
+ The folder needs to contain:
+
+- `nginx.crt`
+
+- `nginx.key`
+
+
+````
+sudo docker run --restart=unless-stopped --name openspeedtest -d -p 3000:3000 -p 3001:3001 openspeedtest/latest
+````
+
+To mount a folder with your own SSL certificate to this Docker container, append the following line to the above command:
+
+
+````bash
+
+-v /${PATH-TO-YOUR-OWN-SSL-CERTIFICATE}:/etc/ssl/
+
+````
+
+I am adding a folder with nginx.crt and nginx.key from my desktop by using the following command.
+
+````bash
+
+sudo docker run -v /Users/vishnu/Desktop/docker/:/etc/ssl/ --restart=unless-stopped --name openspeedtest -d -p 3000:3000 -p 3001:3001 openspeedtest/latest
+
+````
+#### Or use docker-compose.yml
+````
+version: '3.3'
+services:
+ speedtest:
+ volumes:
+ - '/Users/vishnu/Desktop/docker/:/etc/ssl/'
+ restart: unless-stopped
+ container_name: openspeedtest
+ ports:
+ - '3000:3000'
+ - '3001:3001'
+ image: openspeedtest/latest
+````
+## Advanced Configuration Options
+
+- Container Port Configuration
+
+To enable port changes, set the `CHANGE_CONTAINER_PORTS` environment variable to `"True"` and provide appropriate values for the following variables.
+
+`CHANGE_CONTAINER_PORTS=True`
+
+`HTTP_PORT=3000`
+
+`HTTPS_PORT=3001`
+
+- Set User
+
+`SET_USER=101`
+
+- Only Allow `CORS Request` from listed domains.
+
+`ALLOW_ONLY=domain1.com;domain2.com;domain3.com`
+
+- `SET_SERVER_NAME` Display the server name on the UI.
+
+`SET_SERVER_NAME=HOME-NAS`
+
+
+Docker images run better on Linux Platforms, including your NAS. But if you install docker on macOS or Windows, you may see poor performance. I asked this on Docker forums, and they told me macOS and Windows support is for Development purposes only. For Production, you need to use any Linux Platform.
+
+The same Story goes for Windows NGINX. Nginx uses only one worker even if you specify n number of worker processes. They will show in Task Manager, but the system will only use one. I got this information directly from the Nginx website.
+
+
+
+
+
+## Why do you need to Create Your Own SpeedTest Server?
+
+You can run OpenSpeedTest Server in your Home Lab, Office Server or Cloud Server. So that you or employees who work from home can run a speed test to your office and make sure they can run everything smoothly.
+
+
+
+**Choosing between ISP1 & ISP2.**
+
+
+
+Sometimes your ISP2 is Faster than ISP1 when you test your speed on popular speed test sites. But when you connect to your Home/Office/Cloud, that slower connection may perform better. The only way to find out is to run a speed test against your infrastructure.
+
+
+
+**Troubleshooting network issues.**
+
+
+
+It is common even when your Internet connection is working fine, but some of the devices in your network may experience trouble getting decent connectivity to the internet. The issue might be the wrong VLAN ID or Faulty Switch. If you run a Local network speed test, you can find and fix these issues easily.
+
+
+
+**Before you add a repeater.**
+
+
+
+Most repeaters will reduce your network speed by 50%, so if you put it far away, it will perform worst, and if you put it too close, you will not get enough coverage if you run a Local Network speed test. Depending on the application requirements, you can decide exactly where you need to put your repeater.
+
+
+
+**Browsing experience.**
+
+
+
+Many useful browser extensions are out there that we all know and love. But some of them are really slowing you down for **few seconds per page you visit**. You may see good performance when you test your network performance via File Transfer or Command-line utilities, but you may experience poor performance when browsing the internet. This is due to a bad browser configuration that including unwanted extensions installed. From my experience, only keep the one you are going to use every single day. Extension that you may use once in a while should be removed or disabled for maximum performance. If you see poor performance, try OpenSpeedTest from Private Window or Incognito Window. **This tool can be used to check the browser performance and impact of Extensions on your browsing experience.**
+
+
+
+**No client-side software or plugin is required. You can run a network speed test from any device with a [Web Browser that is IE10 or newer.](https://www.youtube.com/watch?v=9f-OM_WQ7Bw&list=PLt-deStxFJOMEAs2O1lJhscMNzcg9E3Po&index=1)**
+
+- Like this Project? Please **Donate NOW & Keep us Alive** -> https://go.openspeedtest.com/Donate
+
+MIT License
+
+Copyright (c) 2013 - 2023 OpenSpeedTest™
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/data/agent/var/lib/openflare/pages/deployments/3/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/css/app.css b/data/agent/var/lib/openflare/pages/deployments/3/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/css/app.css
new file mode 100644
index 00000000..39fa9393
--- /dev/null
+++ b/data/agent/var/lib/openflare/pages/deployments/3/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/css/app.css
@@ -0,0 +1,382 @@
+/* roboto-regular - latin */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+ src: url("..fonts/roboto-v30-latin-regular.eot"); /* IE9 Compat Modes */
+ src: local(""),
+ url("../fonts/roboto-v30-latin-regular.eot?#iefix")
+ format("embedded-opentype"),
+ /* IE6-IE8 */ url("../fonts/roboto-v30-latin-regular.woff2")
+ format("woff2"),
+ /* Super Modern Browsers */
+ url("../fonts/roboto-v30-latin-regular.woff") format("woff"),
+ /* Modern Browsers */ url("../fonts/roboto-v30-latin-regular.ttf")
+ format("truetype"),
+ /* Safari, Android, iOS */
+ url("../fonts/roboto-v30-latin-regular.svg#Roboto") format("svg"); /* Legacy iOS */
+}
+/* roboto-500 - latin */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 500;
+ font-display: swap;
+ src: url("assets/fonts/roboto-v30-latin-500.eot"); /* IE9 Compat Modes */
+ src: local(""),
+ url("assets/fonts/roboto-v30-latin-500.eot?#iefix")
+ format("embedded-opentype"),
+ /* IE6-IE8 */ url("../fonts/roboto-v30-latin-500.woff2")
+ format("woff2"),
+ /* Super Modern Browsers */ url("../fonts/roboto-v30-latin-500.woff")
+ format("woff"),
+ /* Modern Browsers */ url("../fonts/roboto-v30-latin-500.ttf")
+ format("truetype"),
+ /* Safari, Android, iOS */
+ url("../fonts/roboto-v30-latin-500.svg#Roboto") format("svg"); /* Legacy iOS */
+}
+
+body {
+ margin: 0px;
+ padding: 0px;
+ display: block;
+}
+
+::-webkit-scrollbar {
+ display: none;
+}
+html {
+ -ms-overflow-style: none;
+ scrollbar-width: none;
+}
+
+.Credits {
+color: rgb(125 119 119);
+text-align: center;
+font-size:14px;
+font-family: Roboto-Medium, Roboto;
+font-weight: 500;
+}
+.Credits a {
+ text-decoration: none;
+ color: rgb(113, 113, 113);
+}
+.Credits a:hover {
+color: #14b0fe;
+}
+
+.ConnectError {
+ display: none;
+
+}
+
+.openSpeedtestApp {
+
+ height: 100vh;
+ width: 100vw;
+ display: none;
+ overflow: hidden;
+}
+.main-Gaugebg {
+ fill: none;
+ stroke: rgb(231, 231, 232);
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-width: 22px;
+ stroke-dasharray: 681;
+}
+.main-GaugeBlue {
+ fill: none;
+ stroke: url(#gradient);
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-width: 22px;
+ stroke-dasharray: 681;
+ stroke-opacity: 0;
+}
+.main-GaugeWhite {
+ fill: none;
+ stroke: rgb(255, 255, 255);
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-width: 15px;
+ stroke-dasharray: 0, 681;
+ stroke-dashoffset: 1;
+ stroke-opacity: 0;
+}
+.oDo-Meter {
+ font-size: 16.633283615112305px;
+ fill: gray;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+}
+.oDoLive-Speed {
+ font-size: 28px;
+ fill: #201e1e;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+
+.oDoLive-Status {
+ font-size: 10px;
+ fill: #d2d1d2;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+.uiBg {
+ fill: #d2d1d2;
+}
+.progressbg {
+ stroke: rgb(231, 231, 232);
+ stroke-width: 8px;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-dasharray: 400;
+ stroke-dashoffset: 0;
+}
+.Cards {
+ fill: #f2f2f2;
+}
+.Symbol {
+ fill: url(#gradient);
+}
+.rtext {
+ font-size: 12px;
+ fill: #333;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+}
+.rtextnum {
+ font-size: 23px;
+ fill: #201e1e;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+.rtextmbms {
+ font-size: 12px;
+ fill: #5f5f5f;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+.jitter-Mob {
+ font-size: 9px;
+ fill: #5f5f5f;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+.startButton {
+ fill: url(#RadialGradient1);
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ -webkit-tap-highlight-color: transparent;
+ cursor: pointer;
+ pointer-events: visible;
+}
+.buttonTxt {
+ font-size: 40px;
+ fill: #ffffff;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+}
+
+.intro-Progress {
+ stroke: #56c4fb;
+ stroke-width: 8px;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-dasharray: 400;
+ stroke-dashoffset: 0;
+}
+.progressElmstart {
+ stroke: #56c4fb;
+ stroke-width: 8px;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-dasharray: 400;
+ stroke-dashoffset: 0;
+ display: block;
+}
+.Startsettings {
+ fill: url(#RadialGradient1);
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ -webkit-tap-highlight-color: transparent;
+ cursor: pointer;
+ pointer-events: visible;
+ opacity: 0.1;
+ transition: opacity 1s ease-in-out;
+}
+.Startsettings:hover {
+ opacity: 1;
+}
+
+.progressbg {
+ stroke: rgb(231, 231, 232);
+ stroke-width: 8px;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-dasharray: 400;
+ stroke-dashoffset: 0;
+}
+.deskStart {
+ fill: none;
+ stroke: rgb(231, 231, 232);
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-width: 22px;
+ stroke-dasharray: 681;
+ stroke: url(#gradient);
+}
+#UI-Desk {
+ display: none;
+}
+#UI-Mob {
+ display: none;
+}
+.oDoTop-Speed {
+ font-size: 16.96px;
+ fill: gray;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: end;
+}
+#upSymbolDesk {
+ fill: #14b0fe;
+ display: none;
+}
+#downSymbolDesk {
+ fill: #14b0fe;
+ display: none;
+}
+#upSymbolMob {
+ fill: #14b0fe;
+ display: none;
+}
+#downSymbolMob {
+ fill: #14b0fe;
+ display: none;
+}
+
+#ipMob {
+ font-size: 15px;
+ fill: #201e1e;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+ display: none;
+}
+#ipDesk {
+ font-size: 15px;
+ fill: #201e1e;
+ font-family: Roboto-Medium, Roboto;
+ font-weight: 500;
+ text-anchor: middle;
+ display: none;
+}
+
+.spinner {
+ position: absolute;
+ z-index: 999;
+ top: 50vh;
+ left: 50vw;
+ text-align: center;
+}
+
+.spinner > div {
+ width: 20px;
+ height: 20px;
+ background-color: #2196f3;
+
+ border-radius: 100%;
+ display: inline-block;
+ -webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
+ animation: sk-bouncedelay 1.4s infinite ease-in-out both;
+}
+
+.spinner .bounce1 {
+ -webkit-animation-delay: -0.32s;
+ animation-delay: -0.32s;
+}
+
+.spinner .bounce2 {
+ -webkit-animation-delay: -0.16s;
+ animation-delay: -0.16s;
+}
+
+@-webkit-keyframes sk-bouncedelay {
+ 0%,
+ 80%,
+ 100% {
+ -webkit-transform: scale(0);
+ }
+ 40% {
+ -webkit-transform: scale(1);
+ }
+}
+
+@keyframes sk-bouncedelay {
+ 0%,
+ 80%,
+ 100% {
+ -webkit-transform: scale(0);
+ transform: scale(0);
+ }
+ 40% {
+ -webkit-transform: scale(1);
+ transform: scale(1);
+ }
+}
+
+.darkmode {
+ margin-bottom: -15px;
+ fill: #75757a99;
+ padding-top: 16px;
+ display: none;
+ cursor: pointer;
+ margin-right: 30px;
+}
+#daymode {
+ margin-right: 40px;
+}
+.darkmode:hover {
+ fill: #000000;
+}
+
+.Mobile,
+.Desktop {
+ visibility: hidden;
+ width: 100vw;
+ height: 100vh;
+}
+
+@media only screen and (orientation: landscape) {
+ .Mobile {
+ visibility: hidden;
+ }
+ .Desktop {
+ visibility: visible;
+ }
+}
+
+@media only screen and (orientation: portrait) {
+ .spinner {
+ top: 42vh;
+ left: 42vw;
+ }
+ .Mobile {
+ visibility: visible;
+ }
+ .Desktop {
+ visibility: hidden;
+ }
+}
+@media only screen and (max-width: 300px) {
+ .Credits{
+ display: none;
+ }
+}
diff --git a/data/agent/var/lib/openflare/pages/deployments/3/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/css/darkmode.css b/data/agent/var/lib/openflare/pages/deployments/3/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/css/darkmode.css
new file mode 100644
index 00000000..14fef300
--- /dev/null
+++ b/data/agent/var/lib/openflare/pages/deployments/3/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/css/darkmode.css
@@ -0,0 +1,45 @@
+body {
+ background-color: #181818;
+}
+#ipDesk {
+ fill: aliceblue;
+}
+
+.oDoLive-Speed {
+ fill: #ffffff;
+}
+.oDoLive-Status {
+ fill: aliceblue;
+}
+#ipMob {
+ fill: aliceblue;
+}
+.rtextnum {
+ fill: #ffffff;
+}
+.rtextmbms {
+ fill: #ffffff;
+}
+.rtext {
+ fill: #ffffff;
+}
+.Cards {
+ fill: #000000;
+}
+
+.main-Gaugebg {
+ stroke: #000000;
+}
+.uiBg {
+ fill: #000000;
+}
+.progressbg {
+ stroke: #202020;
+}
+.jitter-Mob {
+ fill: #ffffff;
+}
+
+.ConnectError {
+ fill: #ffffff;
+}
diff --git a/data/agent/var/lib/openflare/pages/deployments/3/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/fonts/roboto-v30-latin-500.eot b/data/agent/var/lib/openflare/pages/deployments/3/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/fonts/roboto-v30-latin-500.eot
new file mode 100644
index 00000000..fec03612
Binary files /dev/null and b/data/agent/var/lib/openflare/pages/deployments/3/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/fonts/roboto-v30-latin-500.eot differ
diff --git a/data/agent/var/lib/openflare/pages/deployments/3/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/fonts/roboto-v30-latin-500.svg b/data/agent/var/lib/openflare/pages/deployments/3/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/fonts/roboto-v30-latin-500.svg
new file mode 100644
index 00000000..67eecf44
--- /dev/null
+++ b/data/agent/var/lib/openflare/pages/deployments/3/releases/4b4e50095b4b0ea6fac74ad141dae50c3497481a0710b47e30b0f579951d55c6/assets/fonts/roboto-v30-latin-500.svg
@@ -0,0 +1,305 @@
+
+
+© Copyright 2013-2024 OpenSpeedTest™ All Rights Reserved.
+>4!==0&&(l=!1),l){let s=I(c);postMessage({hash:s,data:i,difficulty:e,nonce:t});return}t+=n,a++,t%1!==0&&(t=Math.trunc(t)),r&&(a&1023)===0&&postMessage(t)}});})();
+//# sourceMappingURL=sha256-purejs.mjs.map
diff --git a/openflare-server/internal/agent/nginx/pow_static/js/worker/sha256-webcrypto.mjs b/openflare-server/internal/agent/nginx/pow_static/js/worker/sha256-webcrypto.mjs
new file mode 100644
index 00000000..7d32193b
--- /dev/null
+++ b/openflare-server/internal/agent/nginx/pow_static/js/worker/sha256-webcrypto.mjs
@@ -0,0 +1,32 @@
+/*
+@licstart The following is the entire license notice for the
+JavaScript code in this page.
+
+Copyright (c) 2025 Xe Iaso
Difficulty:",
+ "js_speed": "Speed:",
+ "js_verification_longer": "Verification is taking longer than expected. Please do not refresh the page.",
+ "js_success": "Success!",
+ "js_done_took": "Done! Took",
+ "js_iterations": "iterations",
+ "js_finished_reading": "I've finished reading, continue →",
+ "js_calculation_error": "Calculation error!",
+ "js_calculation_error_msg": "Failed to calculate challenge:"
+}
diff --git a/openflare-server/internal/agent/nginx/pow_static/locales/zh-CN.json b/openflare-server/internal/agent/nginx/pow_static/locales/zh-CN.json
new file mode 100644
index 00000000..19529607
--- /dev/null
+++ b/openflare-server/internal/agent/nginx/pow_static/locales/zh-CN.json
@@ -0,0 +1,66 @@
+{
+ "loading": "加载中...",
+ "why_am_i_seeing": "为什么我会看到这个?",
+ "protected_by": "本网站由",
+ "protected_from": "保护,来自",
+ "made_with": "在 🇨🇦 用 ❤️ 制作",
+ "mascot_design": "吉祥物由",
+ "ai_companies_explanation": "您会看到这个画面,是因为网站管理员启用了 Anubis 来保护服务器,避免 AI 公司大量爬取网站内容。这类行为会导致网站崩溃,让所有用户都无法正常访问资源。",
+ "anubis_compromise": "Anubis 是一种折中做法。它采用了类似 Hashcash 的工作量证明机制(Proof-of-Work),该机制最初是为了减少垃圾邮件而提出。其核心概念是:对个别用户而言,额外的计算负担可以忽略,但对大规模爬虫来说,累积起来的成本将大幅增加,从而让爬取行为变得更困难。",
+ "hack_purpose": "最终,这是一个占位符解决方案,以便将更多时间用于指纹识别和识别无头浏览器(例如:通过它们如何进行字体渲染),从而无需向更可能是合法用户的用户呈现挑战工作量证明页面。",
+ "jshelter_note": "请注意,Anubis 需要使用现代 JavaScript 功能,而像 JShelter 这类插件可能会阻挡这些功能。请为此域名停用 JShelter 或类似的插件。",
+ "version_info": "这个网站正在运行的 Anubis 版本为",
+ "try_again": "再试一次",
+ "go_home": "返回首页",
+ "contact_webmaster": "或者您觉得您不应该被封锁,请联系网站管理员于",
+ "connection_security": "请稍等,我们需要在继续之前检查您的连接安全性。",
+ "javascript_required": "很遗憾,您必须启用 JavaScript 才能通过这项验证。这是因为 AI 公司已经改变了网站托管的社会契约,因此我们必须采取这样的保护机制。无需 JavaScript 的解决方案仍在开发中。",
+ "benchmark_requires_js": "运行基准测试工具需要启用 JavaScript。",
+ "difficulty": "难度:",
+ "algorithm": "算法:",
+ "compare": "比较:",
+ "time": "时间",
+ "iters": "迭代",
+ "time_a": "时间 A",
+ "iters_a": "迭代 A",
+ "time_b": "时间 B",
+ "iters_b": "迭代 B",
+ "static_check_endpoint": "这是提供给您的反向代理服务器使用的检查端点。",
+ "authorization_required": "需要认证",
+ "cookies_disabled": "您的浏览器目前已禁用 Cookie,为了确认您是合法用户,Anubis 需要启用 Cookie。 请您为此域名启用 Cookie",
+ "access_denied": "拒绝访问:错误代码",
+ "dronebl_entry": "DroneBL 报告了一条记录",
+ "see_dronebl_lookup": "见",
+ "internal_server_error": "内部服务器错误:管理员错误地配置了 Anubis。 请联系管理员要求他们检查日志",
+ "invalid_redirect": "无效的重定向",
+ "redirect_not_parseable": "重定向 URL 无法解析",
+ "redirect_domain_not_allowed": "重定向的域名并不允许",
+ "failed_to_sign_jwt": "签署 JWT 失败",
+ "invalid_invocation": "无效的 MakeChallenge 调用",
+ "client_error_browser": "客户端错误:请确保您的浏览器是最新版本并稍候再试。",
+ "oh_noes": "哎呀糟糕了!",
+ "benchmarking_anubis": "正在进行 Anubis 性能测试!",
+ "you_are_not_a_bot": "你不是机器人!",
+ "making_sure_not_bot": "正在确认你是不是机器人!",
+ "celphase": "CELPHASE 设计",
+ "js_web_crypto_error": "您的浏览器无法正常使用 web.crypto 组件。您是否通过安全连接(HTTPS)查看此网站?",
+ "js_web_workers_error": "您的浏览器并不支持 Web workers (Anubis 使用这个来避免冻结您的浏览器 )您有安装像是 JShelter 之类的插件吗?",
+ "js_cookies_error": "您的浏览器无法存储 Cookie。 Anubis 会使用 Cookie 存储签署的凭证,以判断用户是否已通过验证。请为此域名启用 Cookie 存储功能。 请注意,Anubis 存储的 Cookie 名称可能会变动,且其名称与内容不属于公开 API 的一部分。",
+ "js_context_not_secure": "您的内容并不安全",
+ "js_context_not_secure_msg": "请尝试使用 HTTPS 连接,或联系网站管理员设置 HTTPS。更多信息请参见 MDN。",
+ "js_calculating": "计算中...",
+ "js_missing_feature": "缺少功能",
+ "js_challenge_error": "挑战错误!",
+ "js_challenge_error_msg": "解决检查算法失败。 您可能会想要刷新页面。",
+ "js_calculating_difficulty": "计算中...
难度:",
+ "js_speed": "速度:",
+ "js_verification_longer": "验证所花的时间高于预期。 请不要刷新页面。",
+ "js_success": "成功!",
+ "js_done_took": "完成! 花费",
+ "js_iterations": "迭代",
+ "js_finished_reading": "我读完了,继续 →",
+ "js_calculation_error": "计算错误!",
+ "js_calculation_error_msg": "计算挑战失败:",
+ "missing_required_forwarded_headers": "缺少必要的 X-Forwarded-* 头",
+ "simplified_explanation": "这是一种类似于验证码的措施,用于防止机器人和恶意请求。但是,您无需自己动手,您的浏览器会收到一个计算任务,必须解决该任务以确保它是有效的客户端。这个概念称为工作量证明。该任务在几秒钟内计算完毕,您将被授予访问网站的权限。感谢您的理解和耐心。"
+}
diff --git a/openflare-agent/internal/nginx/waf_assets.go b/openflare-server/internal/agent/nginx/waf_assets.go
similarity index 99%
rename from openflare-agent/internal/nginx/waf_assets.go
rename to openflare-server/internal/agent/nginx/waf_assets.go
index 18ddfc39..3edd8953 100644
--- a/openflare-agent/internal/nginx/waf_assets.go
+++ b/openflare-server/internal/agent/nginx/waf_assets.go
@@ -1,6 +1,6 @@
package nginx
-import "github.com/rain-kl/openflare/openflare-agent/internal/protocol"
+import "github.com/Rain-kl/Wavelet/internal/agent/protocol"
const openRestyWAFRuntimeLua = `local _M = {}
diff --git a/openflare-agent/internal/observability/collector.go b/openflare-server/internal/agent/observability/collector.go
similarity index 97%
rename from openflare-agent/internal/observability/collector.go
rename to openflare-server/internal/agent/observability/collector.go
index a9b13a61..1ced9b97 100644
--- a/openflare-agent/internal/observability/collector.go
+++ b/openflare-server/internal/agent/observability/collector.go
@@ -13,9 +13,9 @@ import (
"syscall"
"time"
- "github.com/rain-kl/openflare/openflare-agent/internal/config"
- "github.com/rain-kl/openflare/openflare-agent/internal/protocol"
- "github.com/rain-kl/openflare/openflare-agent/internal/state"
+ "github.com/Rain-kl/Wavelet/internal/agent/config"
+ "github.com/Rain-kl/Wavelet/internal/agent/protocol"
+ "github.com/Rain-kl/Wavelet/internal/agent/state"
)
func BuildProfile(cfg *config.Config, stateStore *state.Store) *protocol.NodeSystemProfile {
diff --git a/openflare-agent/internal/observability/openresty_local.go b/openflare-server/internal/agent/observability/openresty_local.go
similarity index 96%
rename from openflare-agent/internal/observability/openresty_local.go
rename to openflare-server/internal/agent/observability/openresty_local.go
index c2357ddc..65702eea 100644
--- a/openflare-agent/internal/observability/openresty_local.go
+++ b/openflare-server/internal/agent/observability/openresty_local.go
@@ -10,8 +10,8 @@ import (
"strings"
"time"
- "github.com/rain-kl/openflare/openflare-agent/internal/config"
- "github.com/rain-kl/openflare/openflare-agent/internal/protocol"
+ "github.com/Rain-kl/Wavelet/internal/agent/config"
+ "github.com/Rain-kl/Wavelet/internal/agent/protocol"
)
const openRestyObservabilityPath = "/openflare/observability"
diff --git a/openflare-agent/internal/observability/openresty_local_test.go b/openflare-server/internal/agent/observability/openresty_local_test.go
similarity index 97%
rename from openflare-agent/internal/observability/openresty_local_test.go
rename to openflare-server/internal/agent/observability/openresty_local_test.go
index 81e213e4..dcfe7d35 100644
--- a/openflare-agent/internal/observability/openresty_local_test.go
+++ b/openflare-server/internal/agent/observability/openresty_local_test.go
@@ -7,7 +7,7 @@ import (
"strings"
"testing"
- "github.com/rain-kl/openflare/openflare-agent/internal/config"
+ "github.com/Rain-kl/Wavelet/internal/agent/config"
)
func TestCollectManagedOpenRestyMetrics(t *testing.T) {
diff --git a/openflare-agent/internal/observability/traffic.go b/openflare-server/internal/agent/observability/traffic.go
similarity index 98%
rename from openflare-agent/internal/observability/traffic.go
rename to openflare-server/internal/agent/observability/traffic.go
index 7ceed5d4..a1b425c5 100644
--- a/openflare-agent/internal/observability/traffic.go
+++ b/openflare-server/internal/agent/observability/traffic.go
@@ -13,9 +13,9 @@ import (
"strings"
"time"
- "github.com/rain-kl/openflare/openflare-agent/internal/config"
- "github.com/rain-kl/openflare/openflare-agent/internal/protocol"
- "github.com/rain-kl/openflare/openflare-agent/internal/state"
+ "github.com/Rain-kl/Wavelet/internal/agent/config"
+ "github.com/Rain-kl/Wavelet/internal/agent/protocol"
+ "github.com/Rain-kl/Wavelet/internal/agent/state"
)
type accessLogRecord struct {
diff --git a/openflare-agent/internal/observability/traffic_test.go b/openflare-server/internal/agent/observability/traffic_test.go
similarity index 97%
rename from openflare-agent/internal/observability/traffic_test.go
rename to openflare-server/internal/agent/observability/traffic_test.go
index 700d5135..86892dfe 100644
--- a/openflare-agent/internal/observability/traffic_test.go
+++ b/openflare-server/internal/agent/observability/traffic_test.go
@@ -6,9 +6,9 @@ import (
"strings"
"testing"
- "github.com/rain-kl/openflare/openflare-agent/internal/config"
- "github.com/rain-kl/openflare/openflare-agent/internal/protocol"
- "github.com/rain-kl/openflare/openflare-agent/internal/state"
+ "github.com/Rain-kl/Wavelet/internal/agent/config"
+ "github.com/Rain-kl/Wavelet/internal/agent/protocol"
+ "github.com/Rain-kl/Wavelet/internal/agent/state"
)
func TestBuildTrafficReportAggregatesManagedAccessLog(t *testing.T) {
diff --git a/openflare-agent/internal/protocol/agent_api.go b/openflare-server/internal/agent/protocol/agent_api.go
similarity index 99%
rename from openflare-agent/internal/protocol/agent_api.go
rename to openflare-server/internal/agent/protocol/agent_api.go
index dafc1939..66c8ef3c 100644
--- a/openflare-agent/internal/protocol/agent_api.go
+++ b/openflare-server/internal/agent/protocol/agent_api.go
@@ -204,4 +204,4 @@ type WAFIPGroupSyncResponse struct {
type SupportFile struct {
Path string `json:"path"`
Content string `json:"content"`
-}
\ No newline at end of file
+}
diff --git a/openflare-agent/internal/state/observability_buffer.go b/openflare-server/internal/agent/state/observability_buffer.go
similarity index 99%
rename from openflare-agent/internal/state/observability_buffer.go
rename to openflare-server/internal/agent/state/observability_buffer.go
index 21a1436c..9748be23 100644
--- a/openflare-agent/internal/state/observability_buffer.go
+++ b/openflare-server/internal/agent/state/observability_buffer.go
@@ -8,7 +8,7 @@ import (
"strconv"
"sync"
- "github.com/rain-kl/openflare/openflare-agent/internal/protocol"
+ "github.com/Rain-kl/Wavelet/internal/agent/protocol"
)
const observabilityBufferWindowSeconds = 60
diff --git a/openflare-agent/internal/state/observability_buffer_test.go b/openflare-server/internal/agent/state/observability_buffer_test.go
similarity index 98%
rename from openflare-agent/internal/state/observability_buffer_test.go
rename to openflare-server/internal/agent/state/observability_buffer_test.go
index 5764f8cb..f2c2ccc1 100644
--- a/openflare-agent/internal/state/observability_buffer_test.go
+++ b/openflare-server/internal/agent/state/observability_buffer_test.go
@@ -4,7 +4,7 @@ import (
"path/filepath"
"testing"
- "github.com/rain-kl/openflare/openflare-agent/internal/protocol"
+ "github.com/Rain-kl/Wavelet/internal/agent/protocol"
)
func TestObservabilityBufferStoreUpsertReplayAndAck(t *testing.T) {
diff --git a/openflare-agent/internal/state/state.go b/openflare-server/internal/agent/state/state.go
similarity index 100%
rename from openflare-agent/internal/state/state.go
rename to openflare-server/internal/agent/state/state.go
diff --git a/openflare-agent/internal/state/state_test.go b/openflare-server/internal/agent/state/state_test.go
similarity index 100%
rename from openflare-agent/internal/state/state_test.go
rename to openflare-server/internal/agent/state/state_test.go
diff --git a/openflare-agent/internal/sync/pages.go b/openflare-server/internal/agent/sync/pages.go
similarity index 99%
rename from openflare-agent/internal/sync/pages.go
rename to openflare-server/internal/agent/sync/pages.go
index 3faa89ac..22dd5c4b 100644
--- a/openflare-agent/internal/sync/pages.go
+++ b/openflare-server/internal/agent/sync/pages.go
@@ -15,7 +15,7 @@ import (
"path/filepath"
"strings"
- "github.com/rain-kl/openflare/openflare-agent/internal/protocol"
+ "github.com/Rain-kl/Wavelet/internal/agent/protocol"
)
type pagesSourceDocument struct {
diff --git a/openflare-agent/internal/sync/service.go b/openflare-server/internal/agent/sync/service.go
similarity index 98%
rename from openflare-agent/internal/sync/service.go
rename to openflare-server/internal/agent/sync/service.go
index d7d24351..de12c879 100644
--- a/openflare-agent/internal/sync/service.go
+++ b/openflare-server/internal/agent/sync/service.go
@@ -11,11 +11,11 @@ import (
"sort"
"strings"
- openrestyrender "github.com/rain-kl/openflare/pkg/render/openresty"
+ openrestyrender "github.com/Rain-kl/Wavelet/pkg/render/openresty"
- "github.com/rain-kl/openflare/openflare-agent/internal/nginx"
- "github.com/rain-kl/openflare/openflare-agent/internal/protocol"
- "github.com/rain-kl/openflare/openflare-agent/internal/state"
+ "github.com/Rain-kl/Wavelet/internal/agent/nginx"
+ "github.com/Rain-kl/Wavelet/internal/agent/protocol"
+ "github.com/Rain-kl/Wavelet/internal/agent/state"
)
const (
diff --git a/openflare-agent/internal/sync/service_test.go b/openflare-server/internal/agent/sync/service_test.go
similarity index 99%
rename from openflare-agent/internal/sync/service_test.go
rename to openflare-server/internal/agent/sync/service_test.go
index 033bc623..8aed0c7b 100644
--- a/openflare-agent/internal/sync/service_test.go
+++ b/openflare-server/internal/agent/sync/service_test.go
@@ -13,9 +13,9 @@ import (
"testing"
"time"
- "github.com/rain-kl/openflare/openflare-agent/internal/nginx"
- "github.com/rain-kl/openflare/openflare-agent/internal/protocol"
- "github.com/rain-kl/openflare/openflare-agent/internal/state"
+ "github.com/Rain-kl/Wavelet/internal/agent/nginx"
+ "github.com/Rain-kl/Wavelet/internal/agent/protocol"
+ "github.com/Rain-kl/Wavelet/internal/agent/state"
)
type fakeExecutor struct {
diff --git a/openflare-agent/internal/updater/restart_unix.go b/openflare-server/internal/agent/updater/restart_unix.go
similarity index 100%
rename from openflare-agent/internal/updater/restart_unix.go
rename to openflare-server/internal/agent/updater/restart_unix.go
diff --git a/openflare-agent/internal/updater/restart_unix_test.go b/openflare-server/internal/agent/updater/restart_unix_test.go
similarity index 100%
rename from openflare-agent/internal/updater/restart_unix_test.go
rename to openflare-server/internal/agent/updater/restart_unix_test.go
diff --git a/openflare-agent/internal/updater/restart_windows.go b/openflare-server/internal/agent/updater/restart_windows.go
similarity index 100%
rename from openflare-agent/internal/updater/restart_windows.go
rename to openflare-server/internal/agent/updater/restart_windows.go
diff --git a/openflare-agent/internal/updater/updater.go b/openflare-server/internal/agent/updater/updater.go
similarity index 98%
rename from openflare-agent/internal/updater/updater.go
rename to openflare-server/internal/agent/updater/updater.go
index 486c2b21..b1f6144b 100644
--- a/openflare-agent/internal/updater/updater.go
+++ b/openflare-server/internal/agent/updater/updater.go
@@ -14,10 +14,10 @@ import (
"strings"
"time"
- "github.com/rain-kl/openflare/pkg/utils"
+ "github.com/Rain-kl/Wavelet/pkg/utils"
- "github.com/rain-kl/openflare/openflare-agent/internal/agent"
- "github.com/rain-kl/openflare/openflare-agent/internal/config"
+ "github.com/Rain-kl/Wavelet/internal/agent/agent"
+ "github.com/Rain-kl/Wavelet/internal/agent/config"
)
const maxChecksumAssetSize = 64 * 1024
diff --git a/openflare-agent/internal/updater/updater_test.go b/openflare-server/internal/agent/updater/updater_test.go
similarity index 98%
rename from openflare-agent/internal/updater/updater_test.go
rename to openflare-server/internal/agent/updater/updater_test.go
index 43b29e41..c7f0b8c7 100644
--- a/openflare-agent/internal/updater/updater_test.go
+++ b/openflare-server/internal/agent/updater/updater_test.go
@@ -12,8 +12,8 @@ import (
"strings"
"testing"
- "github.com/rain-kl/openflare/openflare-agent/internal/agent"
- "github.com/rain-kl/openflare/openflare-agent/internal/config"
+ "github.com/Rain-kl/Wavelet/internal/agent/agent"
+ "github.com/Rain-kl/Wavelet/internal/agent/config"
)
type roundTripFunc func(req *http.Request) (*http.Response, error)
diff --git a/openflare-agent/internal/wsclient/client.go b/openflare-server/internal/agent/wsclient/client.go
similarity index 93%
rename from openflare-agent/internal/wsclient/client.go
rename to openflare-server/internal/agent/wsclient/client.go
index a6584b6a..371f9d5f 100644
--- a/openflare-agent/internal/wsclient/client.go
+++ b/openflare-server/internal/agent/wsclient/client.go
@@ -4,8 +4,8 @@ import (
"context"
"time"
- "github.com/rain-kl/openflare/openflare-agent/internal/protocol"
- shared "github.com/rain-kl/openflare/pkg/wsclient"
+ "github.com/Rain-kl/Wavelet/internal/agent/protocol"
+ shared "github.com/Rain-kl/Wavelet/pkg/wsclient"
)
type WSMessage = shared.WSMessage
diff --git a/openflare-server/internal/apps/admin/push/custom_events/admin_login_test.go b/openflare-server/internal/apps/admin/push/custom_events/admin_login_test.go
index 4fc0881b..a7687b07 100644
--- a/openflare-server/internal/apps/admin/push/custom_events/admin_login_test.go
+++ b/openflare-server/internal/apps/admin/push/custom_events/admin_login_test.go
@@ -13,6 +13,7 @@ import (
"github.com/Rain-kl/Wavelet/internal/apps/admin/push"
"github.com/Rain-kl/Wavelet/internal/listener"
"github.com/Rain-kl/Wavelet/internal/model"
+ "github.com/Rain-kl/Wavelet/internal/repository"
"github.com/Rain-kl/Wavelet/internal/task"
"github.com/Rain-kl/Wavelet/internal/testhelper"
"github.com/hibiken/asynq"
@@ -166,6 +167,7 @@ func TestAdminLoginPushIntegration(t *testing.T) {
require.NoError(t, dbConn.Where("event_key = ?", AdminLogin.Key).First(&event).Error)
event.Enabled = false
require.NoError(t, dbConn.Save(&event).Error)
+ repository.DeleteActivePushEventCache(context.Background(), AdminLogin.Key)
listener.EmitAdminLoggedIn(context.Background(), adminUser, "10.0.0.1")
waitForAsyncTrigger(t)
diff --git a/openflare-server/internal/apps/admin/push/push_test.go b/openflare-server/internal/apps/admin/push/push_test.go
index 4464f975..705c4cb7 100644
--- a/openflare-server/internal/apps/admin/push/push_test.go
+++ b/openflare-server/internal/apps/admin/push/push_test.go
@@ -25,7 +25,9 @@ import (
"github.com/stretchr/testify/require"
"gorm.io/gorm"
+ "github.com/Rain-kl/Wavelet/internal/apps/oauth"
"github.com/Rain-kl/Wavelet/internal/common/response"
+ "github.com/Rain-kl/Wavelet/internal/repository"
)
var adminLoginEvent = EventMetadata{
@@ -191,6 +193,7 @@ func TestEventTrigger(t *testing.T) {
event.Targets = []string{"admin_user"}
err = dbConn.Save(&event).Error
require.NoError(t, err)
+ repository.DeleteActivePushEventCache(context.Background(), "admin_login")
// Trigger
body := map[string]any{
@@ -249,6 +252,7 @@ func TestEventTrigger(t *testing.T) {
event.Targets = []string{"user.username"} // 动态目标
err = dbConn.Save(&event).Error
require.NoError(t, err)
+ repository.DeleteActivePushEventCache(context.Background(), "admin_login")
// Trigger with empty body (simulates cron scheduler triggering)
DefaultTrigger.Trigger(context.Background(), adminLoginEvent, nil)
@@ -372,6 +376,7 @@ func TestPushRouters(t *testing.T) {
// 2. 为该事件关联渠道后,再切换开启,应当成功
event.Channels = []string{"email"}
dbConn.Save(&event)
+ repository.DeleteActivePushEventCache(context.Background(), event.EventKey)
req2, _ := http.NewRequest("POST", "/api/v1/admin/push/events/"+strconv.FormatUint(event.ID, 10)+"/toggle", nil)
w2 := httptest.NewRecorder()
diff --git a/openflare-server/internal/apps/cap/routers_test.go b/openflare-server/internal/apps/cap/routers_test.go
index a99da38c..4f72d2ed 100644
--- a/openflare-server/internal/apps/cap/routers_test.go
+++ b/openflare-server/internal/apps/cap/routers_test.go
@@ -37,6 +37,15 @@ func TestCapEndpointsAndMiddleware(t *testing.T) {
c.JSON(http.StatusOK, response.OK("login success"))
})
+ // Ensure CAPTCHA is disabled initially for step 2
+ if err := sqliteDB.Model(&model.SystemConfig{}).Where("key = ?", model.ConfigKeyCapLoginEnabled).Update("value", "false").Error; err != nil {
+ t.Fatalf("failed to disable cap_login_enabled in DB: %v", err)
+ }
+ if err := repository.InvalidateSystemConfigCache(context.Background(), model.ConfigKeyCapLoginEnabled); err != nil {
+ t.Fatalf("InvalidateSystemConfigCache() error = %v", err)
+ }
+ InvalidateRuntimeSettings()
+
// 1. Test challenge generation
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/api/cap/challenge", nil)
diff --git a/openflare-server/internal/apps/openflare/agent/access_log_region.go b/openflare-server/internal/apps/openflare/agent/access_log_region.go
index f3823535..2e9dd76b 100644
--- a/openflare-server/internal/apps/openflare/agent/access_log_region.go
+++ b/openflare-server/internal/apps/openflare/agent/access_log_region.go
@@ -8,7 +8,7 @@ import (
"net"
"strings"
- pkggeoip "github.com/rain-kl/openflare/pkg/geoip"
+ pkggeoip "github.com/Rain-kl/Wavelet/pkg/geoip"
)
var accessLogGeoProviderFactory = func() (pkggeoip.GeoIPService, error) {
diff --git a/openflare-server/internal/apps/openflare/agent/config.go b/openflare-server/internal/apps/openflare/agent/config.go
index 122697f9..7ee88edf 100644
--- a/openflare-server/internal/apps/openflare/agent/config.go
+++ b/openflare-server/internal/apps/openflare/agent/config.go
@@ -11,7 +11,7 @@ import (
"time"
"github.com/Rain-kl/Wavelet/internal/db"
- openrestyrender "github.com/rain-kl/openflare/pkg/render/openresty"
+ openrestyrender "github.com/Rain-kl/Wavelet/pkg/render/openresty"
"gorm.io/gorm"
)
diff --git a/openflare-server/internal/apps/openflare/agent/config_test.go b/openflare-server/internal/apps/openflare/agent/config_test.go
index 2a4f2c8e..8da277ce 100644
--- a/openflare-server/internal/apps/openflare/agent/config_test.go
+++ b/openflare-server/internal/apps/openflare/agent/config_test.go
@@ -6,7 +6,7 @@ package agent
import (
"testing"
- openrestyrender "github.com/rain-kl/openflare/pkg/render/openresty"
+ openrestyrender "github.com/Rain-kl/Wavelet/pkg/render/openresty"
)
func TestIsRuntimeGeneratedSupportFile(t *testing.T) {
diff --git a/openflare-server/internal/apps/openflare/agent/helpers_test.go b/openflare-server/internal/apps/openflare/agent/helpers_test.go
index a7e9ec23..98e00c88 100644
--- a/openflare-server/internal/apps/openflare/agent/helpers_test.go
+++ b/openflare-server/internal/apps/openflare/agent/helpers_test.go
@@ -8,7 +8,7 @@ import (
"testing"
"github.com/Rain-kl/Wavelet/internal/model"
- pkggeoip "github.com/rain-kl/openflare/pkg/geoip"
+ pkggeoip "github.com/Rain-kl/Wavelet/pkg/geoip"
)
type fakeGeoIPProvider struct {
diff --git a/openflare-server/internal/apps/openflare/config_version/renderer.go b/openflare-server/internal/apps/openflare/config_version/renderer.go
index a5a70e2a..e3112ea7 100644
--- a/openflare-server/internal/apps/openflare/config_version/renderer.go
+++ b/openflare-server/internal/apps/openflare/config_version/renderer.go
@@ -4,7 +4,7 @@
package config_version
import (
- openrestyrender "github.com/rain-kl/openflare/pkg/render/openresty"
+ openrestyrender "github.com/Rain-kl/Wavelet/pkg/render/openresty"
)
// SupportFile is a rendered configuration support artifact.
diff --git a/openflare-server/internal/apps/openflare/config_version/snapshot.go b/openflare-server/internal/apps/openflare/config_version/snapshot.go
index 8909237b..6912d98a 100644
--- a/openflare-server/internal/apps/openflare/config_version/snapshot.go
+++ b/openflare-server/internal/apps/openflare/config_version/snapshot.go
@@ -13,7 +13,7 @@ import (
"github.com/Rain-kl/Wavelet/internal/apps/openflare/waf"
"github.com/Rain-kl/Wavelet/internal/model"
- openrestyrender "github.com/rain-kl/openflare/pkg/render/openresty"
+ openrestyrender "github.com/Rain-kl/Wavelet/pkg/render/openresty"
)
type snapshotRoute struct {
diff --git a/openflare-server/internal/apps/openflare/flared/middleware_test.go b/openflare-server/internal/apps/openflare/flared/middleware_test.go
index cffd3148..8dbe26d4 100644
--- a/openflare-server/internal/apps/openflare/flared/middleware_test.go
+++ b/openflare-server/internal/apps/openflare/flared/middleware_test.go
@@ -9,6 +9,7 @@ import (
"net/http/httptest"
"testing"
+ "github.com/Rain-kl/Wavelet/internal/common/response"
"github.com/Rain-kl/Wavelet/internal/db"
"github.com/Rain-kl/Wavelet/internal/model"
"github.com/gin-gonic/gin"
@@ -53,6 +54,7 @@ func TestTunnelAuthMissingToken(t *testing.T) {
gin.SetMode(gin.TestMode)
engine := gin.New()
+ engine.Use(response.ErrorHandlerMiddleware())
engine.GET("/flared/test", TunnelAuth(), func(c *gin.Context) {
c.Status(http.StatusOK)
})
@@ -71,6 +73,7 @@ func TestTunnelAuthRejectsWrongNodeType(t *testing.T) {
gin.SetMode(gin.TestMode)
engine := gin.New()
+ engine.Use(response.ErrorHandlerMiddleware())
engine.GET("/flared/test", TunnelAuth(), func(c *gin.Context) {
c.Status(http.StatusOK)
})
diff --git a/openflare-server/internal/apps/openflare/geoip/lookup.go b/openflare-server/internal/apps/openflare/geoip/lookup.go
index dcd892bb..3ce1f190 100644
--- a/openflare-server/internal/apps/openflare/geoip/lookup.go
+++ b/openflare-server/internal/apps/openflare/geoip/lookup.go
@@ -9,7 +9,7 @@ import (
"net"
"strings"
- pkggeoip "github.com/rain-kl/openflare/pkg/geoip"
+ pkggeoip "github.com/Rain-kl/Wavelet/pkg/geoip"
)
// LookupView is the legacy OpenFlare GeoIP lookup response shape.
diff --git a/openflare-server/internal/apps/openflare/geoip/lookup_test.go b/openflare-server/internal/apps/openflare/geoip/lookup_test.go
index 02467be7..3ea878a0 100644
--- a/openflare-server/internal/apps/openflare/geoip/lookup_test.go
+++ b/openflare-server/internal/apps/openflare/geoip/lookup_test.go
@@ -7,7 +7,7 @@ import (
"net"
"testing"
- pkggeoip "github.com/rain-kl/openflare/pkg/geoip"
+ pkggeoip "github.com/Rain-kl/Wavelet/pkg/geoip"
)
type fakeLookupProvider struct{}
diff --git a/openflare-server/internal/apps/openflare/relay/middleware_test.go b/openflare-server/internal/apps/openflare/relay/middleware_test.go
index 492b5565..163e0ec1 100644
--- a/openflare-server/internal/apps/openflare/relay/middleware_test.go
+++ b/openflare-server/internal/apps/openflare/relay/middleware_test.go
@@ -9,6 +9,7 @@ import (
"net/http/httptest"
"testing"
+ "github.com/Rain-kl/Wavelet/internal/common/response"
"github.com/Rain-kl/Wavelet/internal/db"
"github.com/Rain-kl/Wavelet/internal/model"
"github.com/gin-gonic/gin"
@@ -53,6 +54,7 @@ func TestRelayAuthMissingToken(t *testing.T) {
gin.SetMode(gin.TestMode)
engine := gin.New()
+ engine.Use(response.ErrorHandlerMiddleware())
engine.GET("/relay/test", RelayAuth(), func(c *gin.Context) {
c.Status(http.StatusOK)
})
@@ -71,6 +73,7 @@ func TestRelayAuthRejectsWrongNodeType(t *testing.T) {
gin.SetMode(gin.TestMode)
engine := gin.New()
+ engine.Use(response.ErrorHandlerMiddleware())
engine.GET("/relay/test", RelayAuth(), func(c *gin.Context) {
c.Status(http.StatusOK)
})
diff --git a/openflare-server/internal/apps/user/routers_test.go b/openflare-server/internal/apps/user/routers_test.go
index 79828c8d..6699b6aa 100644
--- a/openflare-server/internal/apps/user/routers_test.go
+++ b/openflare-server/internal/apps/user/routers_test.go
@@ -139,6 +139,11 @@ func TestRegisterCreatesAuthenticatedEncryptedUser(t *testing.T) {
dbConn, _, cleanup := testhelper.SetupTestEnvironment(t)
defer cleanup()
+ // Enable registration for this test
+ dbConn.Model(&model.SystemConfig{}).Where("key = ?", model.ConfigKeyRegistrationEnabled).Update("value", "true")
+ dbConn.Model(&model.SystemConfig{}).Where("key = ?", model.ConfigKeyPasswordRegisterEnabled).Update("value", "true")
+ _ = repository.InvalidateAllSystemConfigCaches(context.Background())
+
router := setupUserTestRouter(t)
payload := registerRequest{
Username: "newuser",
diff --git a/openflare-server/internal/db/migrator/migrator_test.go b/openflare-server/internal/db/migrator/migrator_test.go
index 56a1241f..1f061bba 100644
--- a/openflare-server/internal/db/migrator/migrator_test.go
+++ b/openflare-server/internal/db/migrator/migrator_test.go
@@ -104,11 +104,11 @@ func TestMigrateClearsStaleSystemConfigCache(t *testing.T) {
})
staleConfig := model.SystemConfig{
- Key: model.ConfigKeyCapLoginEnabled,
+ Key: model.ConfigKeyEmailLoginVerificationEnabled,
Value: "true",
Type: "system",
}
- if err := db.HSetJSON(context.Background(), repository.SystemConfigRedisHashKey, model.ConfigKeyCapLoginEnabled, &staleConfig); err != nil {
+ if err := db.HSetJSON(context.Background(), repository.SystemConfigRedisHashKey, model.ConfigKeyEmailLoginVerificationEnabled, &staleConfig); err != nil {
t.Fatalf("HSetJSON() error = %v", err)
}
@@ -122,11 +122,11 @@ func TestMigrateClearsStaleSystemConfigCache(t *testing.T) {
t.Fatalf("system config cache exists = %d, want 0", exists)
}
- enabled, err := repository.GetBoolByKey(context.Background(), model.ConfigKeyCapLoginEnabled)
+ enabled, err := repository.GetBoolByKey(context.Background(), model.ConfigKeyEmailLoginVerificationEnabled)
if err != nil {
- t.Fatalf("GetBoolByKey(%s) error = %v", model.ConfigKeyCapLoginEnabled, err)
+ t.Fatalf("GetBoolByKey(%s) error = %v", model.ConfigKeyEmailLoginVerificationEnabled, err)
}
if enabled {
- t.Fatalf("GetBoolByKey(%s) = true, want false", model.ConfigKeyCapLoginEnabled)
+ t.Fatalf("GetBoolByKey(%s) = true, want false", model.ConfigKeyEmailLoginVerificationEnabled)
}
}
diff --git a/openflared/internal/config/config.go b/openflare-server/internal/flared/config/config.go
similarity index 100%
rename from openflared/internal/config/config.go
rename to openflare-server/internal/flared/config/config.go
diff --git a/openflare-relay/internal/config/version.go b/openflare-server/internal/flared/config/version.go
similarity index 100%
rename from openflare-relay/internal/config/version.go
rename to openflare-server/internal/flared/config/version.go
diff --git a/openflared/internal/flared/runner.go b/openflare-server/internal/flared/flared/runner.go
similarity index 82%
rename from openflared/internal/flared/runner.go
rename to openflare-server/internal/flared/flared/runner.go
index 8dce39ef..52fdbc40 100644
--- a/openflared/internal/flared/runner.go
+++ b/openflare-server/internal/flared/flared/runner.go
@@ -5,12 +5,12 @@ import (
"log/slog"
"time"
- "github.com/rain-kl/openflare/openflared/internal/config"
- "github.com/rain-kl/openflare/openflared/internal/frpc"
- "github.com/rain-kl/openflare/openflared/internal/heartbeat"
- "github.com/rain-kl/openflare/openflared/internal/httpclient"
- "github.com/rain-kl/openflare/openflared/internal/sync"
- "github.com/rain-kl/openflare/openflared/internal/wsclient"
+ "github.com/Rain-kl/Wavelet/internal/flared/config"
+ "github.com/Rain-kl/Wavelet/internal/flared/frpc"
+ "github.com/Rain-kl/Wavelet/internal/flared/heartbeat"
+ "github.com/Rain-kl/Wavelet/internal/flared/httpclient"
+ "github.com/Rain-kl/Wavelet/internal/flared/sync"
+ "github.com/Rain-kl/Wavelet/internal/flared/wsclient"
)
type Runner struct {
diff --git a/openflared/internal/frpc/manager.go b/openflare-server/internal/flared/frpc/manager.go
similarity index 98%
rename from openflared/internal/frpc/manager.go
rename to openflare-server/internal/flared/frpc/manager.go
index 9e4fe066..26bb0432 100644
--- a/openflared/internal/frpc/manager.go
+++ b/openflare-server/internal/flared/frpc/manager.go
@@ -14,8 +14,8 @@ import (
"sync"
"time"
- "github.com/rain-kl/openflare/openflared/internal/config"
- service "github.com/rain-kl/openflare/pkg/protocol"
+ "github.com/Rain-kl/Wavelet/internal/flared/config"
+ service "github.com/Rain-kl/Wavelet/pkg/protocol"
)
type Manager struct {
diff --git a/openflared/internal/frpc/manager_test.go b/openflare-server/internal/flared/frpc/manager_test.go
similarity index 98%
rename from openflared/internal/frpc/manager_test.go
rename to openflare-server/internal/flared/frpc/manager_test.go
index 6d88128a..a94c7609 100644
--- a/openflared/internal/frpc/manager_test.go
+++ b/openflare-server/internal/flared/frpc/manager_test.go
@@ -10,8 +10,8 @@ import (
"testing"
"time"
- "github.com/rain-kl/openflare/openflared/internal/config"
- service "github.com/rain-kl/openflare/pkg/protocol"
+ "github.com/Rain-kl/Wavelet/internal/flared/config"
+ service "github.com/Rain-kl/Wavelet/pkg/protocol"
)
// Helper to write control file for the dummy script
diff --git a/openflared/internal/heartbeat/service.go b/openflare-server/internal/flared/heartbeat/service.go
similarity index 89%
rename from openflared/internal/heartbeat/service.go
rename to openflare-server/internal/flared/heartbeat/service.go
index d01bf583..75f6d2d2 100644
--- a/openflared/internal/heartbeat/service.go
+++ b/openflare-server/internal/flared/heartbeat/service.go
@@ -6,13 +6,13 @@ import (
"net"
"time"
- "github.com/rain-kl/openflare/openflared/internal/config"
- "github.com/rain-kl/openflare/openflared/internal/frpc"
- "github.com/rain-kl/openflare/openflared/internal/httpclient"
- "github.com/rain-kl/openflare/openflared/internal/updater"
- "github.com/rain-kl/openflare/pkg/geoip"
- "github.com/rain-kl/openflare/pkg/geoip/iputil"
- service "github.com/rain-kl/openflare/pkg/protocol"
+ "github.com/Rain-kl/Wavelet/internal/flared/config"
+ "github.com/Rain-kl/Wavelet/internal/flared/frpc"
+ "github.com/Rain-kl/Wavelet/internal/flared/httpclient"
+ "github.com/Rain-kl/Wavelet/internal/flared/updater"
+ "github.com/Rain-kl/Wavelet/pkg/geoip"
+ "github.com/Rain-kl/Wavelet/pkg/geoip/iputil"
+ service "github.com/Rain-kl/Wavelet/pkg/protocol"
)
var (
diff --git a/openflared/internal/httpclient/client.go b/openflare-server/internal/flared/httpclient/client.go
similarity index 98%
rename from openflared/internal/httpclient/client.go
rename to openflare-server/internal/flared/httpclient/client.go
index bf96f855..821bdb35 100644
--- a/openflared/internal/httpclient/client.go
+++ b/openflare-server/internal/flared/httpclient/client.go
@@ -11,7 +11,7 @@ import (
"strings"
"time"
- service "github.com/rain-kl/openflare/pkg/protocol"
+ service "github.com/Rain-kl/Wavelet/pkg/protocol"
)
type APIResponse[T any] struct {
@@ -140,4 +140,4 @@ func readBodyError(body []byte, fallback string) error {
return errors.New(errBody.ErrorMsg)
}
return errors.New(fallback)
-}
\ No newline at end of file
+}
diff --git a/openflared/internal/sync/service.go b/openflare-server/internal/flared/sync/service.go
similarity index 88%
rename from openflared/internal/sync/service.go
rename to openflare-server/internal/flared/sync/service.go
index 576d9683..2783bcff 100644
--- a/openflared/internal/sync/service.go
+++ b/openflare-server/internal/flared/sync/service.go
@@ -5,10 +5,10 @@ import (
"log/slog"
"time"
- "github.com/rain-kl/openflare/openflared/internal/config"
- "github.com/rain-kl/openflare/openflared/internal/frpc"
- "github.com/rain-kl/openflare/openflared/internal/httpclient"
- service "github.com/rain-kl/openflare/pkg/protocol"
+ "github.com/Rain-kl/Wavelet/internal/flared/config"
+ "github.com/Rain-kl/Wavelet/internal/flared/frpc"
+ "github.com/Rain-kl/Wavelet/internal/flared/httpclient"
+ service "github.com/Rain-kl/Wavelet/pkg/protocol"
)
type Service struct {
diff --git a/openflare-relay/internal/updater/restart_unix.go b/openflare-server/internal/flared/updater/restart_unix.go
similarity index 100%
rename from openflare-relay/internal/updater/restart_unix.go
rename to openflare-server/internal/flared/updater/restart_unix.go
diff --git a/openflare-relay/internal/updater/restart_windows.go b/openflare-server/internal/flared/updater/restart_windows.go
similarity index 100%
rename from openflare-relay/internal/updater/restart_windows.go
rename to openflare-server/internal/flared/updater/restart_windows.go
diff --git a/openflared/internal/updater/updater.go b/openflare-server/internal/flared/updater/updater.go
similarity index 99%
rename from openflared/internal/updater/updater.go
rename to openflare-server/internal/flared/updater/updater.go
index dcf11849..f5c406e1 100644
--- a/openflared/internal/updater/updater.go
+++ b/openflare-server/internal/flared/updater/updater.go
@@ -14,9 +14,9 @@ import (
"strings"
"time"
- "github.com/rain-kl/openflare/pkg/utils"
+ "github.com/Rain-kl/Wavelet/pkg/utils"
- "github.com/rain-kl/openflare/openflared/internal/config"
+ "github.com/Rain-kl/Wavelet/internal/flared/config"
)
const maxChecksumAssetSize = 64 * 1024
diff --git a/openflared/internal/wsclient/client.go b/openflare-server/internal/flared/wsclient/client.go
similarity index 93%
rename from openflared/internal/wsclient/client.go
rename to openflare-server/internal/flared/wsclient/client.go
index fce44832..66855de3 100644
--- a/openflared/internal/wsclient/client.go
+++ b/openflare-server/internal/flared/wsclient/client.go
@@ -5,8 +5,8 @@ import (
"encoding/json"
"time"
- service "github.com/rain-kl/openflare/pkg/protocol"
- shared "github.com/rain-kl/openflare/pkg/wsclient"
+ service "github.com/Rain-kl/Wavelet/pkg/protocol"
+ shared "github.com/Rain-kl/Wavelet/pkg/wsclient"
)
type WSMessage = shared.WSMessage
diff --git a/openflare-relay/internal/config/config.go b/openflare-server/internal/relay/config/config.go
similarity index 98%
rename from openflare-relay/internal/config/config.go
rename to openflare-server/internal/relay/config/config.go
index 1d12acc5..1e00ef6f 100644
--- a/openflare-relay/internal/config/config.go
+++ b/openflare-server/internal/relay/config/config.go
@@ -10,8 +10,8 @@ import (
"strings"
"time"
- "github.com/rain-kl/openflare/pkg/geoip"
- "github.com/rain-kl/openflare/pkg/geoip/iputil"
+ "github.com/Rain-kl/Wavelet/pkg/geoip"
+ "github.com/Rain-kl/Wavelet/pkg/geoip/iputil"
)
type MillisecondDuration time.Duration
diff --git a/openflared/internal/config/version.go b/openflare-server/internal/relay/config/version.go
similarity index 100%
rename from openflared/internal/config/version.go
rename to openflare-server/internal/relay/config/version.go
diff --git a/openflare-relay/internal/frps/manager.go b/openflare-server/internal/relay/frps/manager.go
similarity index 99%
rename from openflare-relay/internal/frps/manager.go
rename to openflare-server/internal/relay/frps/manager.go
index 466f28fb..521b1dba 100644
--- a/openflare-relay/internal/frps/manager.go
+++ b/openflare-server/internal/relay/frps/manager.go
@@ -12,7 +12,7 @@ import (
"sync"
"time"
- service "github.com/rain-kl/openflare/pkg/protocol"
+ service "github.com/Rain-kl/Wavelet/pkg/protocol"
)
type Manager struct {
diff --git a/openflare-relay/internal/frps/manager_test.go b/openflare-server/internal/relay/frps/manager_test.go
similarity index 99%
rename from openflare-relay/internal/frps/manager_test.go
rename to openflare-server/internal/relay/frps/manager_test.go
index 81da6cd2..546239dc 100644
--- a/openflare-relay/internal/frps/manager_test.go
+++ b/openflare-server/internal/relay/frps/manager_test.go
@@ -10,7 +10,7 @@ import (
"testing"
"time"
- service "github.com/rain-kl/openflare/pkg/protocol"
+ service "github.com/Rain-kl/Wavelet/pkg/protocol"
)
// Helper to write control file for the dummy script
diff --git a/openflare-relay/internal/heartbeat/service.go b/openflare-server/internal/relay/heartbeat/service.go
similarity index 85%
rename from openflare-relay/internal/heartbeat/service.go
rename to openflare-server/internal/relay/heartbeat/service.go
index 2bfd6afc..fb293f34 100644
--- a/openflare-relay/internal/heartbeat/service.go
+++ b/openflare-server/internal/relay/heartbeat/service.go
@@ -5,13 +5,13 @@ import (
"log/slog"
"time"
- "github.com/rain-kl/openflare/openflare-relay/internal/config"
- "github.com/rain-kl/openflare/openflare-relay/internal/frps"
- "github.com/rain-kl/openflare/openflare-relay/internal/httpclient"
- "github.com/rain-kl/openflare/openflare-relay/internal/observability"
- "github.com/rain-kl/openflare/openflare-relay/internal/state"
- "github.com/rain-kl/openflare/openflare-relay/internal/updater"
- service "github.com/rain-kl/openflare/pkg/protocol"
+ "github.com/Rain-kl/Wavelet/internal/relay/config"
+ "github.com/Rain-kl/Wavelet/internal/relay/frps"
+ "github.com/Rain-kl/Wavelet/internal/relay/httpclient"
+ "github.com/Rain-kl/Wavelet/internal/relay/observability"
+ "github.com/Rain-kl/Wavelet/internal/relay/state"
+ "github.com/Rain-kl/Wavelet/internal/relay/updater"
+ service "github.com/Rain-kl/Wavelet/pkg/protocol"
)
type Service struct {
diff --git a/openflare-relay/internal/httpclient/client.go b/openflare-server/internal/relay/httpclient/client.go
similarity index 98%
rename from openflare-relay/internal/httpclient/client.go
rename to openflare-server/internal/relay/httpclient/client.go
index cd61ed10..d580326a 100644
--- a/openflare-relay/internal/httpclient/client.go
+++ b/openflare-server/internal/relay/httpclient/client.go
@@ -11,7 +11,7 @@ import (
"strings"
"time"
- service "github.com/rain-kl/openflare/pkg/protocol"
+ service "github.com/Rain-kl/Wavelet/pkg/protocol"
)
type APIResponse[T any] struct {
@@ -121,4 +121,4 @@ func readBodyError(body []byte, fallback string) error {
return errors.New(errBody.ErrorMsg)
}
return errors.New(fallback)
-}
\ No newline at end of file
+}
diff --git a/openflare-relay/internal/observability/collector.go b/openflare-server/internal/relay/observability/collector.go
similarity index 97%
rename from openflare-relay/internal/observability/collector.go
rename to openflare-server/internal/relay/observability/collector.go
index 5b89e6a5..c04ea0dc 100644
--- a/openflare-relay/internal/observability/collector.go
+++ b/openflare-server/internal/relay/observability/collector.go
@@ -13,10 +13,10 @@ import (
"syscall"
"time"
- "github.com/rain-kl/openflare/openflare-relay/internal/config"
- "github.com/rain-kl/openflare/openflare-relay/internal/frps"
- "github.com/rain-kl/openflare/openflare-relay/internal/state"
- service "github.com/rain-kl/openflare/pkg/protocol"
+ "github.com/Rain-kl/Wavelet/internal/relay/config"
+ "github.com/Rain-kl/Wavelet/internal/relay/frps"
+ "github.com/Rain-kl/Wavelet/internal/relay/state"
+ service "github.com/Rain-kl/Wavelet/pkg/protocol"
)
func BuildProfile(cfg *config.Config, stateStore *state.Store) *service.AgentNodeSystemProfile {
diff --git a/openflare-relay/internal/relay/runner.go b/openflare-server/internal/relay/relay/runner.go
similarity index 80%
rename from openflare-relay/internal/relay/runner.go
rename to openflare-server/internal/relay/relay/runner.go
index 5d589aa9..b4e79509 100644
--- a/openflare-relay/internal/relay/runner.go
+++ b/openflare-server/internal/relay/relay/runner.go
@@ -6,13 +6,13 @@ import (
"log/slog"
"time"
- "github.com/rain-kl/openflare/openflare-relay/internal/config"
- "github.com/rain-kl/openflare/openflare-relay/internal/frps"
- "github.com/rain-kl/openflare/openflare-relay/internal/heartbeat"
- "github.com/rain-kl/openflare/openflare-relay/internal/httpclient"
- "github.com/rain-kl/openflare/openflare-relay/internal/state"
- "github.com/rain-kl/openflare/openflare-relay/internal/wsclient"
- service "github.com/rain-kl/openflare/pkg/protocol"
+ "github.com/Rain-kl/Wavelet/internal/relay/config"
+ "github.com/Rain-kl/Wavelet/internal/relay/frps"
+ "github.com/Rain-kl/Wavelet/internal/relay/heartbeat"
+ "github.com/Rain-kl/Wavelet/internal/relay/httpclient"
+ "github.com/Rain-kl/Wavelet/internal/relay/state"
+ "github.com/Rain-kl/Wavelet/internal/relay/wsclient"
+ service "github.com/Rain-kl/Wavelet/pkg/protocol"
)
type Runner struct {
diff --git a/openflare-relay/internal/state/store.go b/openflare-server/internal/relay/state/store.go
similarity index 100%
rename from openflare-relay/internal/state/store.go
rename to openflare-server/internal/relay/state/store.go
diff --git a/openflared/internal/updater/restart_unix.go b/openflare-server/internal/relay/updater/restart_unix.go
similarity index 100%
rename from openflared/internal/updater/restart_unix.go
rename to openflare-server/internal/relay/updater/restart_unix.go
diff --git a/openflared/internal/updater/restart_windows.go b/openflare-server/internal/relay/updater/restart_windows.go
similarity index 100%
rename from openflared/internal/updater/restart_windows.go
rename to openflare-server/internal/relay/updater/restart_windows.go
diff --git a/openflare-relay/internal/updater/updater.go b/openflare-server/internal/relay/updater/updater.go
similarity index 98%
rename from openflare-relay/internal/updater/updater.go
rename to openflare-server/internal/relay/updater/updater.go
index a386c97b..9c044578 100644
--- a/openflare-relay/internal/updater/updater.go
+++ b/openflare-server/internal/relay/updater/updater.go
@@ -14,9 +14,9 @@ import (
"strings"
"time"
- "github.com/rain-kl/openflare/pkg/utils"
+ "github.com/Rain-kl/Wavelet/pkg/utils"
- "github.com/rain-kl/openflare/openflare-relay/internal/config"
+ "github.com/Rain-kl/Wavelet/internal/relay/config"
)
const maxChecksumAssetSize = 64 * 1024
diff --git a/openflare-relay/internal/wsclient/client.go b/openflare-server/internal/relay/wsclient/client.go
similarity index 93%
rename from openflare-relay/internal/wsclient/client.go
rename to openflare-server/internal/relay/wsclient/client.go
index 81c089a5..db795417 100644
--- a/openflare-relay/internal/wsclient/client.go
+++ b/openflare-server/internal/relay/wsclient/client.go
@@ -5,8 +5,8 @@ import (
"encoding/json"
"time"
- service "github.com/rain-kl/openflare/pkg/protocol"
- shared "github.com/rain-kl/openflare/pkg/wsclient"
+ service "github.com/Rain-kl/Wavelet/pkg/protocol"
+ shared "github.com/Rain-kl/Wavelet/pkg/wsclient"
)
type WSMessage = shared.WSMessage
diff --git a/pkg/geoip/emptyProvider.go b/openflare-server/pkg/geoip/emptyProvider.go
similarity index 100%
rename from pkg/geoip/emptyProvider.go
rename to openflare-server/pkg/geoip/emptyProvider.go
diff --git a/pkg/geoip/geoip.go b/openflare-server/pkg/geoip/geoip.go
similarity index 100%
rename from pkg/geoip/geoip.go
rename to openflare-server/pkg/geoip/geoip.go
diff --git a/pkg/geoip/geoip_test.go b/openflare-server/pkg/geoip/geoip_test.go
similarity index 100%
rename from pkg/geoip/geoip_test.go
rename to openflare-server/pkg/geoip/geoip_test.go
diff --git a/pkg/geoip/geojs.go b/openflare-server/pkg/geoip/geojs.go
similarity index 100%
rename from pkg/geoip/geojs.go
rename to openflare-server/pkg/geoip/geojs.go
diff --git a/pkg/geoip/ipapi.go b/openflare-server/pkg/geoip/ipapi.go
similarity index 100%
rename from pkg/geoip/ipapi.go
rename to openflare-server/pkg/geoip/ipapi.go
diff --git a/pkg/geoip/ipinfo.go b/openflare-server/pkg/geoip/ipinfo.go
similarity index 100%
rename from pkg/geoip/ipinfo.go
rename to openflare-server/pkg/geoip/ipinfo.go
diff --git a/pkg/geoip/iputil/iputil.go b/openflare-server/pkg/geoip/iputil/iputil.go
similarity index 100%
rename from pkg/geoip/iputil/iputil.go
rename to openflare-server/pkg/geoip/iputil/iputil.go
diff --git a/pkg/geoip/iputil/iputil_test.go b/openflare-server/pkg/geoip/iputil/iputil_test.go
similarity index 100%
rename from pkg/geoip/iputil/iputil_test.go
rename to openflare-server/pkg/geoip/iputil/iputil_test.go
diff --git a/pkg/geoip/mmdb.go b/openflare-server/pkg/geoip/mmdb.go
similarity index 100%
rename from pkg/geoip/mmdb.go
rename to openflare-server/pkg/geoip/mmdb.go
diff --git a/pkg/geoip/outboundip.go b/openflare-server/pkg/geoip/outboundip.go
similarity index 98%
rename from pkg/geoip/outboundip.go
rename to openflare-server/pkg/geoip/outboundip.go
index dc1976f1..718b862a 100644
--- a/pkg/geoip/outboundip.go
+++ b/openflare-server/pkg/geoip/outboundip.go
@@ -11,7 +11,7 @@ import (
"strings"
"time"
- "github.com/rain-kl/openflare/pkg/geoip/iputil"
+ "github.com/Rain-kl/Wavelet/pkg/geoip/iputil"
)
const defaultOutboundIPLookupTimeout = 5 * time.Second
diff --git a/pkg/geoip/outboundip_test.go b/openflare-server/pkg/geoip/outboundip_test.go
similarity index 100%
rename from pkg/geoip/outboundip_test.go
rename to openflare-server/pkg/geoip/outboundip_test.go
diff --git a/pkg/protocol/server.go b/openflare-server/pkg/protocol/server.go
similarity index 100%
rename from pkg/protocol/server.go
rename to openflare-server/pkg/protocol/server.go
diff --git a/pkg/render/openresty/render.go b/openflare-server/pkg/render/openresty/render.go
similarity index 100%
rename from pkg/render/openresty/render.go
rename to openflare-server/pkg/render/openresty/render.go
diff --git a/pkg/render/openresty/render_test.go b/openflare-server/pkg/render/openresty/render_test.go
similarity index 100%
rename from pkg/render/openresty/render_test.go
rename to openflare-server/pkg/render/openresty/render_test.go
diff --git a/pkg/render/openresty/types.go b/openflare-server/pkg/render/openresty/types.go
similarity index 100%
rename from pkg/render/openresty/types.go
rename to openflare-server/pkg/render/openresty/types.go
diff --git a/pkg/utils/format.go b/openflare-server/pkg/utils/format.go
similarity index 100%
rename from pkg/utils/format.go
rename to openflare-server/pkg/utils/format.go
diff --git a/pkg/utils/network.go b/openflare-server/pkg/utils/network.go
similarity index 100%
rename from pkg/utils/network.go
rename to openflare-server/pkg/utils/network.go
diff --git a/pkg/utils/slice.go b/openflare-server/pkg/utils/slice.go
similarity index 100%
rename from pkg/utils/slice.go
rename to openflare-server/pkg/utils/slice.go
diff --git a/pkg/utils/string.go b/openflare-server/pkg/utils/string.go
similarity index 100%
rename from pkg/utils/string.go
rename to openflare-server/pkg/utils/string.go
diff --git a/pkg/utils/value.go b/openflare-server/pkg/utils/value.go
similarity index 100%
rename from pkg/utils/value.go
rename to openflare-server/pkg/utils/value.go
diff --git a/pkg/utils/version.go b/openflare-server/pkg/utils/version.go
similarity index 100%
rename from pkg/utils/version.go
rename to openflare-server/pkg/utils/version.go
diff --git a/pkg/wsclient/client.go b/openflare-server/pkg/wsclient/client.go
similarity index 100%
rename from pkg/wsclient/client.go
rename to openflare-server/pkg/wsclient/client.go
diff --git a/openflared/.gitignore b/openflared/.gitignore
deleted file mode 100644
index 5fac6285..00000000
--- a/openflared/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/data/
\ No newline at end of file