update
Some checks failed
Build Gurty / Build Gurty (, ubuntu-latest, linux, x86_64-unknown-linux-gnu) (push) Failing after 1m33s
Build GurtCA / Build GurtCA (, ubuntu-latest, linux, x86_64-unknown-linux-gnu) (push) Failing after 11m20s
Build GDExtension / Build GDExtension (libgurt_godot.so, ubuntu-latest, linux, x86_64-unknown-linux-gnu) (push) Failing after 16m9s
Build Flumi / Build Flumi (Linux, 4.4.1, ubuntu-latest, linux) (push) Failing after 2h10m11s
Build Flumi / Build Flumi (Windows Desktop, 4.4.1, windows-latest, windows) (push) Has been cancelled
Build GDExtension / Build GDExtension (gurt_godot.dll, windows-latest, windows, x86_64-pc-windows-msvc) (push) Has been cancelled
Build GurtCA / Build GurtCA (.exe, windows-latest, windows, x86_64-pc-windows-msvc) (push) Has been cancelled
Build Gurty / Build Gurty (.exe, windows-latest, windows, x86_64-pc-windows-msvc) (push) Has been cancelled
Some checks failed
Build Gurty / Build Gurty (, ubuntu-latest, linux, x86_64-unknown-linux-gnu) (push) Failing after 1m33s
Build GurtCA / Build GurtCA (, ubuntu-latest, linux, x86_64-unknown-linux-gnu) (push) Failing after 11m20s
Build GDExtension / Build GDExtension (libgurt_godot.so, ubuntu-latest, linux, x86_64-unknown-linux-gnu) (push) Failing after 16m9s
Build Flumi / Build Flumi (Linux, 4.4.1, ubuntu-latest, linux) (push) Failing after 2h10m11s
Build Flumi / Build Flumi (Windows Desktop, 4.4.1, windows-latest, windows) (push) Has been cancelled
Build GDExtension / Build GDExtension (gurt_godot.dll, windows-latest, windows, x86_64-pc-windows-msvc) (push) Has been cancelled
Build GurtCA / Build GurtCA (.exe, windows-latest, windows, x86_64-pc-windows-msvc) (push) Has been cancelled
Build Gurty / Build Gurty (.exe, windows-latest, windows, x86_64-pc-windows-msvc) (push) Has been cancelled
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
[Website](https://gurted.com/) | [Docs](https://docs.gurted.com/) | [License](LICENSE) | [YouTube video](https://www.youtube.com)
|
[Website](https://gurted.com/) | [Docs](https://docs.gurted.com/) | [License](LICENSE) | [YouTube video](https://www.youtube.com)
|
||||||
|
|
||||||
Gurted is an ecosystem similar to the World Wide Web, it features:
|
Gurted is an ecosystem similar to the World Wide Web, it features:
|
||||||
- ⚡ A custom protocol (TCP-based) named `GURT://` with mandatory TLS security with a [spec](docs.gurted.com)
|
- ⚡ A custom protocol (TCP-based) named `lw://` with mandatory TLS security with a [spec](docs.gurted.com)
|
||||||
- 🌐 A custom **wayfinder** (browser) written in Rust and GDScript with [Godot](https://godotengine.org/)
|
- 🌐 A custom **wayfinder** (browser) written in Rust and GDScript with [Godot](https://godotengine.org/)
|
||||||
- 📄 A custom engine for HTML, CSS, and ***Lua*** (no JavaScript)
|
- 📄 A custom engine for HTML, CSS, and ***Lua*** (no JavaScript)
|
||||||
- 🏷️ A custom **DNS** that allows users to create domains with TLDs such as `.based`, `.aura`, `.twin`, and many more
|
- 🏷️ A custom **DNS** that allows users to create domains with TLDs such as `.based`, `.aura`, `.twin`, and many more
|
||||||
@@ -16,7 +16,7 @@ Gurted is an ecosystem similar to the World Wide Web, it features:
|
|||||||
# File structure
|
# File structure
|
||||||
- `/dns` - The **DNS** (Domain Name System)
|
- `/dns` - The **DNS** (Domain Name System)
|
||||||
- `/docs` - The **documentation** at https://docs.gurted.com
|
- `/docs` - The **documentation** at https://docs.gurted.com
|
||||||
- `/flumi` - The **wayfinder** Flumi, used to view gurt:// sites
|
- `/flumi` - The **wayfinder** Flumi, used to view lw:// sites
|
||||||
- `/protocol` - All protocol related things
|
- `/protocol` - All protocol related things
|
||||||
- `/protocol/library` - The Rust protocol implementation (client + server)
|
- `/protocol/library` - The Rust protocol implementation (client + server)
|
||||||
- `/protocol/gdextension` - The Godot extension for GURT protocol (uses Rust library, used in Flumi)
|
- `/protocol/gdextension` - The Godot extension for GURT protocol (uses Rust library, used in Flumi)
|
||||||
|
|||||||
2
dns/Cargo.lock
generated
2
dns/Cargo.lock
generated
@@ -973,7 +973,7 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gurtlib"
|
name = "gurtlib"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ end
|
|||||||
|
|
||||||
local function loadDomains()
|
local function loadDomains()
|
||||||
print('Loading domains...')
|
print('Loading domains...')
|
||||||
local response = fetch('gurt://dns.web/auth/domains?page=1&limit=100', {
|
local response = fetch('lw://dns.web/auth/domains?page=1&limit=100', {
|
||||||
headers = {
|
headers = {
|
||||||
Authorization = 'Bearer ' .. authToken
|
Authorization = 'Bearer ' .. authToken
|
||||||
}
|
}
|
||||||
@@ -95,7 +95,7 @@ local function checkAuth()
|
|||||||
|
|
||||||
if authToken then
|
if authToken then
|
||||||
print('Found auth token, checking validity...')
|
print('Found auth token, checking validity...')
|
||||||
local response = fetch('gurt://dns.web/auth/me', {
|
local response = fetch('lw://dns.web/auth/me', {
|
||||||
headers = {
|
headers = {
|
||||||
Authorization = 'Bearer ' .. authToken
|
Authorization = 'Bearer ' .. authToken
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ local renderRecords
|
|||||||
local function deleteRecord(recordId)
|
local function deleteRecord(recordId)
|
||||||
print('Deleting DNS record: ' .. recordId)
|
print('Deleting DNS record: ' .. recordId)
|
||||||
|
|
||||||
local response = fetch('gurt://dns.web/domain/' .. domainName .. '/records/' .. recordId, {
|
local response = fetch('lw://dns.web/domain/' .. domainName .. '/records/' .. recordId, {
|
||||||
method = 'DELETE',
|
method = 'DELETE',
|
||||||
headers = {
|
headers = {
|
||||||
Authorization = 'Bearer ' .. authToken
|
Authorization = 'Bearer ' .. authToken
|
||||||
@@ -57,7 +57,7 @@ end
|
|||||||
-- Actual implementation
|
-- Actual implementation
|
||||||
loadRecords = function()
|
loadRecords = function()
|
||||||
print('Loading DNS records for: ' .. domainName)
|
print('Loading DNS records for: ' .. domainName)
|
||||||
local response = fetch('gurt://dns.web/domain/' .. domainName .. '/records', {
|
local response = fetch('lw://dns.web/domain/' .. domainName .. '/records', {
|
||||||
headers = {
|
headers = {
|
||||||
Authorization = 'Bearer ' .. authToken
|
Authorization = 'Bearer ' .. authToken
|
||||||
}
|
}
|
||||||
@@ -175,7 +175,7 @@ end
|
|||||||
|
|
||||||
local function loadDomain()
|
local function loadDomain()
|
||||||
print('Loading domain details for: ' .. domainName)
|
print('Loading domain details for: ' .. domainName)
|
||||||
local response = fetch('gurt://dns.web/domain/' .. domainName, {
|
local response = fetch('lw://dns.web/domain/' .. domainName, {
|
||||||
headers = {
|
headers = {
|
||||||
Authorization = 'Bearer ' .. authToken
|
Authorization = 'Bearer ' .. authToken
|
||||||
}
|
}
|
||||||
@@ -197,7 +197,7 @@ local function checkAuth()
|
|||||||
|
|
||||||
if authToken then
|
if authToken then
|
||||||
print('Found auth token, checking validity...')
|
print('Found auth token, checking validity...')
|
||||||
local response = fetch('gurt://dns.web/auth/me', {
|
local response = fetch('lw://dns.web/auth/me', {
|
||||||
headers = {
|
headers = {
|
||||||
Authorization = 'Bearer ' .. authToken
|
Authorization = 'Bearer ' .. authToken
|
||||||
}
|
}
|
||||||
@@ -228,7 +228,7 @@ end
|
|||||||
local function addRecord(type, name, value, ttl)
|
local function addRecord(type, name, value, ttl)
|
||||||
hideError('record-error')
|
hideError('record-error')
|
||||||
|
|
||||||
local response = fetch('gurt://dns.web/domain/' .. domainName .. '/records', {
|
local response = fetch('lw://dns.web/domain/' .. domainName .. '/records', {
|
||||||
method = 'POST',
|
method = 'POST',
|
||||||
headers = {
|
headers = {
|
||||||
['Content-Type'] = 'application/json',
|
['Content-Type'] = 'application/json',
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ end
|
|||||||
|
|
||||||
local function loadTLDs()
|
local function loadTLDs()
|
||||||
print('Loading available TLDs...')
|
print('Loading available TLDs...')
|
||||||
local response = fetch('gurt://dns.web/tlds')
|
local response = fetch('lw://dns.web/tlds')
|
||||||
|
|
||||||
if response:ok() then
|
if response:ok() then
|
||||||
tlds = response:json()
|
tlds = response:json()
|
||||||
@@ -91,7 +91,7 @@ local function checkAuth()
|
|||||||
|
|
||||||
if authToken then
|
if authToken then
|
||||||
print('Found auth token, checking validity...')
|
print('Found auth token, checking validity...')
|
||||||
local response = fetch('gurt://dns.web/auth/me', {
|
local response = fetch('lw://dns.web/auth/me', {
|
||||||
headers = {
|
headers = {
|
||||||
Authorization = 'Bearer ' .. authToken
|
Authorization = 'Bearer ' .. authToken
|
||||||
}
|
}
|
||||||
@@ -127,7 +127,7 @@ local function submitDomain(name, tld)
|
|||||||
hideError('domain-error')
|
hideError('domain-error')
|
||||||
print('Submitting domain: ' .. name .. '.' .. tld)
|
print('Submitting domain: ' .. name .. '.' .. tld)
|
||||||
|
|
||||||
local response = fetch('gurt://dns.web/domain', {
|
local response = fetch('lw://dns.web/domain', {
|
||||||
method = 'POST',
|
method = 'POST',
|
||||||
headers = {
|
headers = {
|
||||||
['Content-Type'] = 'application/json',
|
['Content-Type'] = 'application/json',
|
||||||
@@ -157,7 +157,7 @@ end
|
|||||||
|
|
||||||
local function createInvite()
|
local function createInvite()
|
||||||
print('Creating invite code...')
|
print('Creating invite code...')
|
||||||
local response = fetch('gurt://dns.web/auth/invite', {
|
local response = fetch('lw://dns.web/auth/invite', {
|
||||||
method = 'POST',
|
method = 'POST',
|
||||||
headers = {
|
headers = {
|
||||||
Authorization = 'Bearer ' .. authToken
|
Authorization = 'Bearer ' .. authToken
|
||||||
@@ -184,7 +184,7 @@ local function redeemInvite(code)
|
|||||||
hideError('redeem-error')
|
hideError('redeem-error')
|
||||||
print('Redeeming invite code: ' .. code)
|
print('Redeeming invite code: ' .. code)
|
||||||
|
|
||||||
local response = fetch('gurt://dns.web/auth/redeem-invite', {
|
local response = fetch('lw://dns.web/auth/redeem-invite', {
|
||||||
method = 'POST',
|
method = 'POST',
|
||||||
headers = {
|
headers = {
|
||||||
['Content-Type'] = 'application/json',
|
['Content-Type'] = 'application/json',
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ submitBtn:on('submit', function(event)
|
|||||||
password = password
|
password = password
|
||||||
})
|
})
|
||||||
print(request_body)
|
print(request_body)
|
||||||
local url = 'gurt://dns.web/auth/login'
|
local url = 'lw://dns.web/auth/login'
|
||||||
local headers = {
|
local headers = {
|
||||||
['Content-Type'] = 'application/json'
|
['Content-Type'] = 'application/json'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ submitBtn:on('submit', function(event)
|
|||||||
password = password
|
password = password
|
||||||
})
|
})
|
||||||
|
|
||||||
local url = 'gurt://dns.web/auth/register'
|
local url = 'lw://dns.web/auth/register'
|
||||||
local headers = {
|
local headers = {
|
||||||
['Content-Type'] = 'application/json'
|
['Content-Type'] = 'application/json'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ sidebar_position: 6
|
|||||||
|
|
||||||
# DNS System
|
# DNS System
|
||||||
|
|
||||||
The Gurted ecosystem features a custom DNS system that enables domain resolution for the gurt:// protocol. Unlike traditional DNS, Gurted DNS is designed specifically for the decentralized web ecosystem, providing:
|
The Gurted ecosystem features a custom DNS system that enables domain resolution for the lw:// protocol. Unlike traditional DNS, Gurted DNS is designed specifically for the decentralized web ecosystem, providing:
|
||||||
|
|
||||||
- Domain registration with approval workflows
|
- Domain registration with approval workflows
|
||||||
- DNS record management (A, AAAA, CNAME, TXT)
|
- DNS record management (A, AAAA, CNAME, TXT)
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ sidebar_position: 7
|
|||||||
|
|
||||||
# Flumi (browser)
|
# Flumi (browser)
|
||||||
|
|
||||||
**Flumi** is the official browser for the Gurted ecosystem, built using the Godot game engine. It provides a complete web browsing experience for `gurt://` URLs with custom HTML/CSS rendering, Lua scripting support, and integration with the Gurted DNS system.
|
**Flumi** is the official browser for the Gurted ecosystem, built using the Godot game engine. It provides a complete web browsing experience for `lw://` URLs with custom HTML/CSS rendering, Lua scripting support, and integration with the Gurted DNS system.
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ async fn main() -> Result<()> {
|
|||||||
let client = GurtClient::new();
|
let client = GurtClient::new();
|
||||||
|
|
||||||
// Make a GET request
|
// Make a GET request
|
||||||
let response = client.get("gurt://example.com/").await?;
|
let response = client.get("lw://example.com/").await?;
|
||||||
|
|
||||||
println!("Status: {}", response.status_code);
|
println!("Status: {}", response.status_code);
|
||||||
println!("Body: {}", response.text()?);
|
println!("Body: {}", response.text()?);
|
||||||
@@ -65,7 +65,7 @@ let client = GurtClient::with_config(config);
|
|||||||
### GET Requests
|
### GET Requests
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let response = client.get("gurt://api.example.com/users").await?;
|
let response = client.get("lw://api.example.com/users").await?;
|
||||||
|
|
||||||
if response.is_success() {
|
if response.is_success() {
|
||||||
println!("Success: {}", response.text()?);
|
println!("Success: {}", response.text()?);
|
||||||
@@ -78,7 +78,7 @@ if response.is_success() {
|
|||||||
|
|
||||||
#### Text Data
|
#### Text Data
|
||||||
```rust
|
```rust
|
||||||
let response = client.post("gurt://api.example.com/submit", "Hello, GURT!").await?;
|
let response = client.post("lw://api.example.com/submit", "Hello, GURT!").await?;
|
||||||
```
|
```
|
||||||
|
|
||||||
#### JSON Data
|
#### JSON Data
|
||||||
@@ -90,30 +90,30 @@ let data = json!({
|
|||||||
"email": "john@example.com"
|
"email": "john@example.com"
|
||||||
});
|
});
|
||||||
|
|
||||||
let response = client.post_json("gurt://api.example.com/users", &data).await?;
|
let response = client.post_json("lw://api.example.com/users", &data).await?;
|
||||||
```
|
```
|
||||||
|
|
||||||
### PUT Requests
|
### PUT Requests
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Text data
|
// Text data
|
||||||
let response = client.put("gurt://api.example.com/resource/123", "Updated content").await?;
|
let response = client.put("lw://api.example.com/resource/123", "Updated content").await?;
|
||||||
|
|
||||||
// JSON data
|
// JSON data
|
||||||
let update_data = json!({"status": "completed"});
|
let update_data = json!({"status": "completed"});
|
||||||
let response = client.put_json("gurt://api.example.com/tasks/456", &update_data).await?;
|
let response = client.put_json("lw://api.example.com/tasks/456", &update_data).await?;
|
||||||
```
|
```
|
||||||
|
|
||||||
### DELETE Requests
|
### DELETE Requests
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let response = client.delete("gurt://api.example.com/users/123").await?;
|
let response = client.delete("lw://api.example.com/users/123").await?;
|
||||||
```
|
```
|
||||||
|
|
||||||
### HEAD Requests
|
### HEAD Requests
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let response = client.head("gurt://api.example.com/large-file").await?;
|
let response = client.head("lw://api.example.com/large-file").await?;
|
||||||
|
|
||||||
// Check headers without downloading body
|
// Check headers without downloading body
|
||||||
let content_length = response.headers.get("content-length");
|
let content_length = response.headers.get("content-length");
|
||||||
@@ -122,7 +122,7 @@ let content_length = response.headers.get("content-length");
|
|||||||
### OPTIONS Requests
|
### OPTIONS Requests
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let response = client.options("gurt://api.example.com/endpoint").await?;
|
let response = client.options("lw://api.example.com/endpoint").await?;
|
||||||
|
|
||||||
// Check allowed methods
|
// Check allowed methods
|
||||||
let allowed_methods = response.headers.get("allow");
|
let allowed_methods = response.headers.get("allow");
|
||||||
@@ -132,7 +132,7 @@ let allowed_methods = response.headers.get("allow");
|
|||||||
|
|
||||||
```rust
|
```rust
|
||||||
let patch_data = json!({"name": "Updated Name"});
|
let patch_data = json!({"name": "Updated Name"});
|
||||||
let response = client.patch_json("gurt://api.example.com/users/123", &patch_data).await?;
|
let response = client.patch_json("lw://api.example.com/users/123", &patch_data).await?;
|
||||||
```
|
```
|
||||||
|
|
||||||
## Response Handling
|
## Response Handling
|
||||||
@@ -152,7 +152,7 @@ pub struct GurtResponse {
|
|||||||
### Accessing Response Data
|
### Accessing Response Data
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let response = client.get("gurt://api.example.com/data").await?;
|
let response = client.get("lw://api.example.com/data").await?;
|
||||||
|
|
||||||
// Status information
|
// Status information
|
||||||
println!("Status Code: {}", response.status_code);
|
println!("Status Code: {}", response.status_code);
|
||||||
@@ -201,14 +201,14 @@ All of this happens transparently when you call methods like `get()`, `post()`,
|
|||||||
|
|
||||||
## URL Parsing
|
## URL Parsing
|
||||||
|
|
||||||
The client automatically parses `gurt://` URLs:
|
The client automatically parses `lw://` URLs:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// These are all valid GURT URLs:
|
// These are all valid GURT URLs:
|
||||||
client.get("gurt://example.com/").await?; // Port 4878 (default)
|
client.get("lw://example.com/").await?; // Port 4878 (default)
|
||||||
client.get("gurt://example.com:8080/api").await?; // Custom port
|
client.get("lw://example.com:8080/api").await?; // Custom port
|
||||||
client.get("gurt://192.168.1.100/test").await?; // IP address
|
client.get("lw://192.168.1.100/test").await?; // IP address
|
||||||
client.get("gurt://localhost:4878/dev").await?; // Localhost
|
client.get("lw://localhost:4878/dev").await?; // Localhost
|
||||||
```
|
```
|
||||||
|
|
||||||
### URL Components
|
### URL Components
|
||||||
@@ -225,7 +225,7 @@ The client extracts:
|
|||||||
```rust
|
```rust
|
||||||
use gurtlib::GurtError;
|
use gurtlib::GurtError;
|
||||||
|
|
||||||
match client.get("gurt://invalid-url").await {
|
match client.get("lw://invalid-url").await {
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
// Handle successful response
|
// Handle successful response
|
||||||
}
|
}
|
||||||
@@ -332,7 +332,7 @@ impl ApiClient {
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
let api = ApiClient::new("gurt://api.example.com".to_string());
|
let api = ApiClient::new("lw://api.example.com".to_string());
|
||||||
|
|
||||||
// Create a user
|
// Create a user
|
||||||
let new_user = CreateUser {
|
let new_user = CreateUser {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ sidebar_position: 2
|
|||||||
|
|
||||||
# GURT Protocol
|
# GURT Protocol
|
||||||
|
|
||||||
**GURT** (version 1.0.0) is a TCP-based application protocol designed as an HTTP-like alternative with built-in TLS 1.3 encryption. It serves as the foundation for the Gurted ecosystem, enabling secure communication between clients and servers using the `gurt://` URL scheme.
|
**GURT** (version 1.0.0) is a TCP-based application protocol designed as an HTTP-like alternative with built-in TLS 1.3 encryption. It serves as the foundation for the Gurted ecosystem, enabling secure communication between clients and servers using the `lw://` URL scheme.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -21,12 +21,12 @@ GURT provides a familiar HTTP-like syntax while offering security through mandat
|
|||||||
|
|
||||||
## URL Scheme
|
## URL Scheme
|
||||||
|
|
||||||
GURT uses the `gurt://` URL scheme:
|
GURT uses the `lw://` URL scheme:
|
||||||
|
|
||||||
```
|
```
|
||||||
gurt://example.com/path
|
lw://example.com/path
|
||||||
gurt://192.168.1.100:4878/api/data
|
lw://192.168.1.100:4878/api/data
|
||||||
gurt://localhost:4878/hello
|
lw://localhost:4878/hello
|
||||||
```
|
```
|
||||||
|
|
||||||
The protocol automatically defaults to port 4878.
|
The protocol automatically defaults to port 4878.
|
||||||
@@ -244,13 +244,13 @@ GURT integrates with Gurted's custom DNS system:
|
|||||||
|
|
||||||
### Direct IP Access
|
### Direct IP Access
|
||||||
```
|
```
|
||||||
gurt://192.168.1.100:4878/
|
lw://192.168.1.100:4878/
|
||||||
gurt://localhost:4878/api
|
lw://localhost:4878/api
|
||||||
```
|
```
|
||||||
|
|
||||||
### Domain Resolution
|
### Domain Resolution
|
||||||
```
|
```
|
||||||
gurt://example.real/ # Resolves via Gurted DNS
|
lw://example.real/ # Resolves via Gurted DNS
|
||||||
```
|
```
|
||||||
|
|
||||||
The Gurted DNS server resolves domains in the format `name.tld` to IP addresses, enabling human-readable domain names for GURT services. This is done automatically by your GURT browser and is documented in the [DNS System documentation](./dns-system.md).
|
The Gurted DNS server resolves domains in the format `name.tld` to IP addresses, enabling human-readable domain names for GURT services. This is done automatically by your GURT browser and is documented in the [DNS System documentation](./dns-system.md).
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ async fn main() -> Result<()> {
|
|||||||
Ok(GurtResponse::ok().with_json_body(&users))
|
Ok(GurtResponse::ok().with_json_body(&users))
|
||||||
});
|
});
|
||||||
|
|
||||||
println!("GURT server starting on gurt://127.0.0.1:4878");
|
println!("GURT server starting on lw://127.0.0.1:4878");
|
||||||
server.listen("127.0.0.1:4878").await
|
server.listen("127.0.0.1:4878").await
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -462,7 +462,7 @@ mod tests {
|
|||||||
|
|
||||||
// Test with client
|
// Test with client
|
||||||
let client = GurtClient::new();
|
let client = GurtClient::new();
|
||||||
let response = client.get("gurt://127.0.0.1:9999/test").await.unwrap();
|
let response = client.get("lw://127.0.0.1:9999/test").await.unwrap();
|
||||||
|
|
||||||
assert_eq!(response.status_code, 200);
|
assert_eq!(response.status_code, 200);
|
||||||
assert_eq!(response.text().unwrap(), "test response");
|
assert_eq!(response.text().unwrap(), "test response");
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ line breaks
|
|||||||
Links can point to external URLs (which open in the user's default browser) or GURT protocol links:
|
Links can point to external URLs (which open in the user's default browser) or GURT protocol links:
|
||||||
```html
|
```html
|
||||||
<a href="https://example.com">External link</a>
|
<a href="https://example.com">External link</a>
|
||||||
<a href="gurt://internal.site">GURT protocol link</a>
|
<a href="lw://internal.site">GURT protocol link</a>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Line Breaks
|
### Line Breaks
|
||||||
@@ -317,7 +317,7 @@ Network image loading with sizing controls:
|
|||||||
|
|
||||||
```html
|
```html
|
||||||
<img src="https://example.com/image.jpg" style="max-w-24 max-h-24 rounded" />
|
<img src="https://example.com/image.jpg" style="max-w-24 max-h-24 rounded" />
|
||||||
<img src="gurt://local.site/image.png" style="w-32 h-32" />
|
<img src="lw://local.site/image.png" style="w-32 h-32" />
|
||||||
```
|
```
|
||||||
|
|
||||||
## Advanced Features
|
## Advanced Features
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ sidebar_position: 1
|
|||||||
# Introduction
|
# Introduction
|
||||||
|
|
||||||
**Gurted** is a project introducing a new web ecosystem, featuring:
|
**Gurted** is a project introducing a new web ecosystem, featuring:
|
||||||
- the **gurt:// protocol**
|
- the **lw:// protocol**
|
||||||
- a custom search engine
|
- a custom search engine
|
||||||
- a custom browser - **Flumi**
|
- a custom browser - **Flumi**
|
||||||
- a custom **DNS** (Domain Name System)
|
- a custom **DNS** (Domain Name System)
|
||||||
@@ -22,7 +22,7 @@ Learn more about the GURT protocol: [Protocol Specification](./gurt-protocol.md)
|
|||||||
Get started by **exploring Gurted sites** or **try creating your first GURT page**.
|
Get started by **exploring Gurted sites** or **try creating your first GURT page**.
|
||||||
|
|
||||||
To get started, download:
|
To get started, download:
|
||||||
- [Flumi](https://gurted.com/download/), the official browser for `gurt://`
|
- [Flumi](https://gurted.com/download/), the official browser for `lw://`
|
||||||
- A *text editor* of choice, we recommend [Visual Studio Code](https://code.visualstudio.com/download)
|
- A *text editor* of choice, we recommend [Visual Studio Code](https://code.visualstudio.com/download)
|
||||||
|
|
||||||
## Components
|
## Components
|
||||||
@@ -34,7 +34,7 @@ Gurted consists of three main components:
|
|||||||
```html
|
```html
|
||||||
<head>
|
<head>
|
||||||
<title>Yo Gurt</title>
|
<title>Yo Gurt</title>
|
||||||
<icon src="gurt://example.real/icon.png">
|
<icon src="lw://example.real/icon.png">
|
||||||
|
|
||||||
<style>...</style>
|
<style>...</style>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ audio:stop() -- Stop and reset
|
|||||||
audio.currentTime = 30.0 -- Seek to 30 seconds
|
audio.currentTime = 30.0 -- Seek to 30 seconds
|
||||||
audio.volume = 0.8 -- Set volume (0.0 - 1.0)
|
audio.volume = 0.8 -- Set volume (0.0 - 1.0)
|
||||||
audio.loop = true -- Enable looping
|
audio.loop = true -- Enable looping
|
||||||
audio.src = 'gurt://new-audio.mp3' -- Change source
|
audio.src = 'lw://new-audio.mp3' -- Change source
|
||||||
|
|
||||||
local duration = audio.duration
|
local duration = audio.duration
|
||||||
local currentPos = audio.currentTime
|
local currentPos = audio.currentTime
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ download_id = gurt.download(url filename)
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- **url** (string): The URL to download from. Supports HTTP, HTTPS, and gurt:// protocols.
|
- **url** (string): The URL to download from. Supports HTTP, HTTPS, and lw:// protocols.
|
||||||
- **filename** (string, optional): The filename to save as. If not provided, the filename will be extracted from the URL or default to "download".
|
- **filename** (string, optional): The filename to save as. If not provided, the filename will be extracted from the URL or default to "download".
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ gurt.location.reload()
|
|||||||
Navigates to a new URL.
|
Navigates to a new URL.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
gurt.location.goto('gurt://example.com/page')
|
gurt.location.goto('lw://example.com/page')
|
||||||
gurt.location.goto('https://external-site.com')
|
gurt.location.goto('https://external-site.com')
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ end
|
|||||||
|
|
||||||
**Supported Methods:** `GET`, `POST`, `PUT`, `DELETE`, `HEAD`, `OPTIONS`, `PATCH`
|
**Supported Methods:** `GET`, `POST`, `PUT`, `DELETE`, `HEAD`, `OPTIONS`, `PATCH`
|
||||||
|
|
||||||
**Relative URLs** are automatically resolved to the current domain with `gurt://` protocol.
|
**Relative URLs** are automatically resolved to the current domain with `lw://` protocol.
|
||||||
|
|
||||||
## WebSocket API
|
## WebSocket API
|
||||||
|
|
||||||
@@ -89,8 +89,8 @@ trace.log(params) -- name%3DJohn%20Doe%26age%3D30
|
|||||||
|
|
||||||
-- Building query strings
|
-- Building query strings
|
||||||
local searchTerm = 'cats & dogs'
|
local searchTerm = 'cats & dogs'
|
||||||
local url = 'gurt://search.com/api?q=' .. urlEncode(searchTerm)
|
local url = 'lw://search.com/api?q=' .. urlEncode(searchTerm)
|
||||||
trace.log(url) -- gurt://search.com/api?q=cats%20%26%20dogs
|
trace.log(url) -- lw://search.com/api?q=cats%20%26%20dogs
|
||||||
```
|
```
|
||||||
|
|
||||||
### urlDecode(string)
|
### urlDecode(string)
|
||||||
|
|||||||
@@ -435,7 +435,7 @@ text = "Flumi"
|
|||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme = ExtResource("2_theme")
|
theme = ExtResource("2_theme")
|
||||||
theme_override_colors/font_color = Color(0.7, 0.7, 0.7, 1)
|
theme_override_colors/font_color = Color(0.7, 0.7, 0.7, 1)
|
||||||
text = "Flumi is the official browser for the Gurted ecosystem, built using the Godot game engine. It provides a complete web browsing experience for gurt:// URLs with custom HTML/CSS rendering, Lua scripting support, and integration with the Gurted DNS system."
|
text = "Flumi is the official browser for the Gurted ecosystem, built using the Godot game engine. It provides a complete web browsing experience for lw:// URLs with custom HTML/CSS rendering, Lua scripting support, and integration with the Gurted DNS system."
|
||||||
autowrap_mode = 3
|
autowrap_mode = 3
|
||||||
|
|
||||||
[node name="ProtocolPanel" type="VBoxContainer" parent="HSplitContainer/Content/ScrollContainer/ContentStack"]
|
[node name="ProtocolPanel" type="VBoxContainer" parent="HSplitContainer/Content/ScrollContainer/ContentStack"]
|
||||||
@@ -463,7 +463,7 @@ layout_mode = 2
|
|||||||
theme = ExtResource("2_theme")
|
theme = ExtResource("2_theme")
|
||||||
theme_override_colors/font_color = Color(1, 1, 1, 1)
|
theme_override_colors/font_color = Color(1, 1, 1, 1)
|
||||||
theme_override_font_sizes/font_size = 20
|
theme_override_font_sizes/font_size = 20
|
||||||
text = "gurt://"
|
text = "lw://"
|
||||||
|
|
||||||
[node name="DescriptionLabel" type="Label" parent="HSplitContainer/Content/ScrollContainer/ContentStack/ProtocolPanel/ProtocolSection/VBoxContainer"]
|
[node name="DescriptionLabel" type="Label" parent="HSplitContainer/Content/ScrollContainer/ContentStack/ProtocolPanel/ProtocolSection/VBoxContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
@@ -503,5 +503,5 @@ text = "Luau Integration"
|
|||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme = ExtResource("2_theme")
|
theme = ExtResource("2_theme")
|
||||||
theme_override_colors/font_color = Color(0.7, 0.7, 0.7, 1)
|
theme_override_colors/font_color = Color(0.7, 0.7, 0.7, 1)
|
||||||
text = "Flumi uses Luau (Lua) for client-side scripting, providing dynamic content manipulation and interactive experiences within gurt:// pages. Similar to how HTTP web browsers user JavaScript."
|
text = "Flumi uses Luau (Lua) for client-side scripting, providing dynamic content manipulation and interactive experiences within lw:// pages. Similar to how HTTP web browsers user JavaScript."
|
||||||
autowrap_mode = 3
|
autowrap_mode = 3
|
||||||
|
|||||||
@@ -421,8 +421,8 @@ text = "默认搜索引擎地址: "
|
|||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
theme_override_styles/normal = SubResource("StyleBoxFlat_input")
|
theme_override_styles/normal = SubResource("StyleBoxFlat_input")
|
||||||
text = "gurt://search.web?q="
|
text = "lw://search.web?q="
|
||||||
placeholder_text = "gurt://search.web?q="
|
placeholder_text = "lw://search.web?q="
|
||||||
|
|
||||||
[node name="PrivacyPanel" type="VBoxContainer" parent="HSplitContainer/Content/ScrollContainer/ContentStack"]
|
[node name="PrivacyPanel" type="VBoxContainer" parent="HSplitContainer/Content/ScrollContainer/ContentStack"]
|
||||||
visible = false
|
visible = false
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ func _start_download(download_id: String, url: String, save_path: String, downlo
|
|||||||
"current_site": download_data.get("current_site", "")
|
"current_site": download_data.get("current_site", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
if url.begins_with("gurt://"):
|
if url.begins_with("lw://"):
|
||||||
_start_gurt_download(download_id, url)
|
_start_gurt_download(download_id, url)
|
||||||
else:
|
else:
|
||||||
_start_http_download(download_id, url)
|
_start_http_download(download_id, url)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ static func set_dns_server(ip_port: String):
|
|||||||
DNS_SERVER_PORT = 4878
|
DNS_SERVER_PORT = 4878
|
||||||
|
|
||||||
static func is_gurt_domain(url: String) -> bool:
|
static func is_gurt_domain(url: String) -> bool:
|
||||||
if url.begins_with("gurt://"):
|
if url.begins_with("lw://"):
|
||||||
return true
|
return true
|
||||||
|
|
||||||
if not url.contains("://"):
|
if not url.contains("://"):
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ var settings_data = {
|
|||||||
"startup_new_tab": true,
|
"startup_new_tab": true,
|
||||||
"startup_specific_page": false,
|
"startup_specific_page": false,
|
||||||
"startup_url": "",
|
"startup_url": "",
|
||||||
"search_engine_url": "gurt://search.web?q=",
|
"search_engine_url": "lw://search.web?q=",
|
||||||
"download_confirmation": true,
|
"download_confirmation": true,
|
||||||
"dns_url": "135.125.163.131:4878"
|
"dns_url": "135.125.163.131:4878"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ func set_active_tab(index: int) -> void:
|
|||||||
if tabs[index].has_content:
|
if tabs[index].has_content:
|
||||||
main.current_domain = tabs[index].current_url
|
main.current_domain = tabs[index].current_url
|
||||||
var display_text = main.current_domain
|
var display_text = main.current_domain
|
||||||
if display_text.begins_with("gurt://"):
|
if display_text.begins_with("lw://"):
|
||||||
display_text = display_text.substr(7)
|
display_text = display_text.substr(7)
|
||||||
main.search_bar.text = display_text
|
main.search_bar.text = display_text
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ static func get_or_create_gurt_client(domain: String) -> GurtProtocolClient:
|
|||||||
|
|
||||||
static func extract_domain_from_url(gurt_url: String) -> String:
|
static func extract_domain_from_url(gurt_url: String) -> String:
|
||||||
var host_domain = gurt_url
|
var host_domain = gurt_url
|
||||||
if host_domain.begins_with("gurt://"):
|
if host_domain.begins_with("lw://"):
|
||||||
host_domain = host_domain.right(-7)
|
host_domain = host_domain.right(-7)
|
||||||
var slash_pos = host_domain.find("/")
|
var slash_pos = host_domain.find("/")
|
||||||
if slash_pos != -1:
|
if slash_pos != -1:
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ var HTML_CONTENT = """
|
|||||||
<title>新标签页</title>
|
<title>新标签页</title>
|
||||||
</head>
|
</head>
|
||||||
<body style="bg-[#323949] text-white font-sans">
|
<body style="bg-[#323949] text-white font-sans">
|
||||||
<postprocess preset="crt"></postprocess>
|
|
||||||
<div style="flex flex-col items-center justify-center w-full mt-12">
|
<div style="flex flex-col items-center justify-center w-full mt-12">
|
||||||
<h1 style="target text-8xl font-bold mb-4 text-[#4a9eff] font-serif font-italic">你好!</h1>
|
<h1 style="target text-8xl font-bold mb-4 text-[#4a9eff] font-serif font-italic">你好!</h1>
|
||||||
<p style="text-lg mb-8 text-[#cccccc]">欢迎使用LeonBrowser</p>
|
<p style="text-lg mb-8 text-[#cccccc]">欢迎使用LeonBrowser</p>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ static func load_font(font_info: Dictionary) -> void:
|
|||||||
|
|
||||||
if src.begins_with("http://") or src.begins_with("https://"):
|
if src.begins_with("http://") or src.begins_with("https://"):
|
||||||
load_web_font(font_info)
|
load_web_font(font_info)
|
||||||
elif src.begins_with("gurt://"):
|
elif src.begins_with("lw://"):
|
||||||
load_gurt_font(font_info)
|
load_gurt_font(font_info)
|
||||||
else:
|
else:
|
||||||
load_local_font(font_info)
|
load_local_font(font_info)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ func fetch_image(url: String) -> ImageTexture:
|
|||||||
var headers: PackedStringArray
|
var headers: PackedStringArray
|
||||||
var response_headers = {}
|
var response_headers = {}
|
||||||
|
|
||||||
if url.begins_with("gurt://"):
|
if url.begins_with("lw://"):
|
||||||
var gurt_body = await fetch_gurt_resource(url, true)
|
var gurt_body = await fetch_gurt_resource(url, true)
|
||||||
if gurt_body.is_empty():
|
if gurt_body.is_empty():
|
||||||
return null
|
return null
|
||||||
@@ -160,7 +160,7 @@ func fetch_external_resource(url: String, base_url: String = "") -> String:
|
|||||||
|
|
||||||
if resolved_url.begins_with("https://"):
|
if resolved_url.begins_with("https://"):
|
||||||
return await fetch_text(resolved_url)
|
return await fetch_text(resolved_url)
|
||||||
elif resolved_url.begins_with("gurt://"):
|
elif resolved_url.begins_with("lw://"):
|
||||||
return fetch_gurt_resource(resolved_url)
|
return fetch_gurt_resource(resolved_url)
|
||||||
else:
|
else:
|
||||||
print("Resource loading error: Only HTTPS and GURT protocols are supported. Attempted: ", resolved_url)
|
print("Resource loading error: Only HTTPS and GURT protocols are supported. Attempted: ", resolved_url)
|
||||||
@@ -171,8 +171,8 @@ func fetch_gurt_resource(url: String, as_binary: bool = false):
|
|||||||
return PackedByteArray() if as_binary else ""
|
return PackedByteArray() if as_binary else ""
|
||||||
|
|
||||||
var gurt_url = url
|
var gurt_url = url
|
||||||
if not gurt_url.begins_with("gurt://"):
|
if not gurt_url.begins_with("lw://"):
|
||||||
gurt_url = "gurt://" + gurt_url
|
gurt_url = "lw://" + gurt_url
|
||||||
|
|
||||||
if gurt_url.contains("localhost"):
|
if gurt_url.contains("localhost"):
|
||||||
gurt_url = gurt_url.replace("localhost", "127.0.0.1")
|
gurt_url = gurt_url.replace("localhost", "127.0.0.1")
|
||||||
|
|||||||
@@ -102,19 +102,19 @@ func load_audio_async(src: String) -> void:
|
|||||||
|
|
||||||
reset_stream_state()
|
reset_stream_state()
|
||||||
|
|
||||||
if src.begins_with("https://") or src.begins_with("gurt://"):
|
if src.begins_with("https://") or src.begins_with("lw://"):
|
||||||
await load_remote_audio(src)
|
await load_remote_audio(src)
|
||||||
else:
|
else:
|
||||||
print("Audio loading error: Only HTTPS and GURT protocols are supported. Attempted: ", src)
|
print("Audio loading error: Only HTTPS and GURT protocols are supported. Attempted: ", src)
|
||||||
return
|
return
|
||||||
|
|
||||||
func load_remote_audio(src: String) -> void:
|
func load_remote_audio(src: String) -> void:
|
||||||
if not (src.begins_with("https://") or src.begins_with("gurt://")):
|
if not (src.begins_with("https://") or src.begins_with("lw://")):
|
||||||
return
|
return
|
||||||
|
|
||||||
if src.begins_with("https://"):
|
if src.begins_with("https://"):
|
||||||
await load_https_audio(src)
|
await load_https_audio(src)
|
||||||
elif src.begins_with("gurt://"):
|
elif src.begins_with("lw://"):
|
||||||
await load_gurt_audio(src)
|
await load_gurt_audio(src)
|
||||||
|
|
||||||
func load_https_audio(src: String) -> void:
|
func load_https_audio(src: String) -> void:
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ static func get_current_domain() -> String:
|
|||||||
|
|
||||||
static func sanitize_domain_for_filename(domain: String) -> String:
|
static func sanitize_domain_for_filename(domain: String) -> String:
|
||||||
# Remove protocol prefix
|
# Remove protocol prefix
|
||||||
if domain.begins_with("gurt://"):
|
if domain.begins_with("lw://"):
|
||||||
domain = domain.substr(7)
|
domain = domain.substr(7)
|
||||||
elif domain.contains("://"):
|
elif domain.contains("://"):
|
||||||
var parts = domain.split("://")
|
var parts = domain.split("://")
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ static func setup_network_api(vm: LuauVM):
|
|||||||
vm.lua_setglobal("fetch")
|
vm.lua_setglobal("fetch")
|
||||||
|
|
||||||
static func resolve_fetch_url(url: String) -> String:
|
static func resolve_fetch_url(url: String) -> String:
|
||||||
if url.begins_with("http://") or url.begins_with("https://") or url.begins_with("gurt://"):
|
if url.begins_with("http://") or url.begins_with("https://") or url.begins_with("lw://"):
|
||||||
return url
|
return url
|
||||||
|
|
||||||
var main_node = Engine.get_main_loop().current_scene
|
var main_node = Engine.get_main_loop().current_scene
|
||||||
@@ -22,9 +22,9 @@ static func resolve_fetch_url(url: String) -> String:
|
|||||||
|
|
||||||
if current_domain.is_empty():
|
if current_domain.is_empty():
|
||||||
if url.begins_with("/"):
|
if url.begins_with("/"):
|
||||||
return "gurt://" + url.substr(1)
|
return "lw://" + url.substr(1)
|
||||||
else:
|
else:
|
||||||
return "gurt://" + url
|
return "lw://" + url
|
||||||
|
|
||||||
return URLUtils.resolve_url(current_domain, url)
|
return URLUtils.resolve_url(current_domain, url)
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ static func _lua_fetch_handler(vm: LuauVM) -> int:
|
|||||||
if vm.lua_gettop() >= 2 and vm.lua_istable(2):
|
if vm.lua_gettop() >= 2 and vm.lua_istable(2):
|
||||||
options = vm.lua_todictionary(2)
|
options = vm.lua_todictionary(2)
|
||||||
|
|
||||||
# Resolve relative URLs and default to gurt:// protocol
|
# Resolve relative URLs and default to lw:// protocol
|
||||||
var url = resolve_fetch_url(original_url)
|
var url = resolve_fetch_url(original_url)
|
||||||
|
|
||||||
# Default options
|
# Default options
|
||||||
@@ -135,7 +135,7 @@ static func _response_ok_handler(vm: LuauVM) -> int:
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
static func make_http_request(url: String, method: String, headers: PackedStringArray, body: String) -> Dictionary:
|
static func make_http_request(url: String, method: String, headers: PackedStringArray, body: String) -> Dictionary:
|
||||||
if url.begins_with("gurt://"):
|
if url.begins_with("lw://"):
|
||||||
return make_gurt_request(url, method, headers, body)
|
return make_gurt_request(url, method, headers, body)
|
||||||
var http_client = HTTPClient.new()
|
var http_client = HTTPClient.new()
|
||||||
var response_data = {
|
var response_data = {
|
||||||
@@ -320,7 +320,7 @@ static func make_gurt_request(url: String, method: String, headers: PackedString
|
|||||||
"body": ""
|
"body": ""
|
||||||
}
|
}
|
||||||
|
|
||||||
var domain_part = url.replace("gurt://", "")
|
var domain_part = url.replace("lw://", "")
|
||||||
if domain_part.contains("/"):
|
if domain_part.contains("/"):
|
||||||
domain_part = domain_part.split("/")[0]
|
domain_part = domain_part.split("/")[0]
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ extends RefCounted
|
|||||||
|
|
||||||
static func resolve_url(base_url: String, relative_url: String) -> String:
|
static func resolve_url(base_url: String, relative_url: String) -> String:
|
||||||
# If relative_url is already absolute, return it as-is
|
# If relative_url is already absolute, return it as-is
|
||||||
if relative_url.begins_with("http://") or relative_url.begins_with("https://") or relative_url.begins_with("gurt://") or relative_url.begins_with("file://"):
|
if relative_url.begins_with("http://") or relative_url.begins_with("https://") or relative_url.begins_with("lw://") or relative_url.begins_with("file://"):
|
||||||
return relative_url
|
return relative_url
|
||||||
|
|
||||||
# If empty, treat as relative to current domain
|
# If empty, treat as relative to current domain
|
||||||
@@ -94,7 +94,7 @@ static func extract_domain(url: String) -> String:
|
|||||||
return ""
|
return ""
|
||||||
|
|
||||||
var clean_url = url
|
var clean_url = url
|
||||||
if clean_url.begins_with("gurt://"):
|
if clean_url.begins_with("lw://"):
|
||||||
clean_url = clean_url.substr(7)
|
clean_url = clean_url.substr(7)
|
||||||
elif clean_url.begins_with("https://"):
|
elif clean_url.begins_with("https://"):
|
||||||
clean_url = clean_url.substr(8)
|
clean_url = clean_url.substr(8)
|
||||||
|
|||||||
@@ -120,8 +120,8 @@ func _on_search_submitted(url: String, add_to_history: bool = true) -> void:
|
|||||||
tab.start_loading()
|
tab.start_loading()
|
||||||
|
|
||||||
var gurt_url = url
|
var gurt_url = url
|
||||||
if not gurt_url.begins_with("gurt://"):
|
if not gurt_url.begins_with("lw://"):
|
||||||
gurt_url = "gurt://" + gurt_url
|
gurt_url = "lw://" + gurt_url
|
||||||
|
|
||||||
await fetch_gurt_content_async(gurt_url, tab, url, add_to_history)
|
await fetch_gurt_content_async(gurt_url, tab, url, add_to_history)
|
||||||
else:
|
else:
|
||||||
@@ -289,7 +289,7 @@ func _on_search_focus_entered() -> void:
|
|||||||
func _on_search_focus_exited() -> void:
|
func _on_search_focus_exited() -> void:
|
||||||
if not current_domain.is_empty():
|
if not current_domain.is_empty():
|
||||||
var display_text = current_domain
|
var display_text = current_domain
|
||||||
if display_text.begins_with("gurt://"):
|
if display_text.begins_with("lw://"):
|
||||||
display_text = display_text.right(-7)
|
display_text = display_text.right(-7)
|
||||||
elif display_text.begins_with("file://"):
|
elif display_text.begins_with("file://"):
|
||||||
display_text = URLUtils.file_url_to_path(display_text)
|
display_text = URLUtils.file_url_to_path(display_text)
|
||||||
@@ -828,7 +828,7 @@ func reload_current_page() -> void:
|
|||||||
_on_search_submitted(current_domain)
|
_on_search_submitted(current_domain)
|
||||||
|
|
||||||
func navigate_to_url(url: String, add_to_history: bool = true) -> void:
|
func navigate_to_url(url: String, add_to_history: bool = true) -> void:
|
||||||
if url.begins_with("gurt://") or url.begins_with("file://"):
|
if url.begins_with("lw://") or url.begins_with("file://"):
|
||||||
_on_search_submitted(url, add_to_history)
|
_on_search_submitted(url, add_to_history)
|
||||||
else:
|
else:
|
||||||
var resolved_url = resolve_url(url)
|
var resolved_url = resolve_url(url)
|
||||||
@@ -837,7 +837,7 @@ func navigate_to_url(url: String, add_to_history: bool = true) -> void:
|
|||||||
func update_search_bar_from_current_domain() -> void:
|
func update_search_bar_from_current_domain() -> void:
|
||||||
if not search_bar.has_focus() and not current_domain.is_empty():
|
if not search_bar.has_focus() and not current_domain.is_empty():
|
||||||
var display_text = current_domain
|
var display_text = current_domain
|
||||||
if display_text.begins_with("gurt://"):
|
if display_text.begins_with("lw://"):
|
||||||
display_text = display_text.right(-7)
|
display_text = display_text.right(-7)
|
||||||
elif display_text.begins_with("file://"):
|
elif display_text.begins_with("file://"):
|
||||||
display_text = URLUtils.file_url_to_path(display_text)
|
display_text = URLUtils.file_url_to_path(display_text)
|
||||||
@@ -878,7 +878,7 @@ func add_to_history(url: String, tab: Tab, add_to_navigation: bool = true):
|
|||||||
icon_url = tab.get_meta("parsed_icon_url")
|
icon_url = tab.get_meta("parsed_icon_url")
|
||||||
|
|
||||||
var clean_url = url
|
var clean_url = url
|
||||||
if clean_url.begins_with("gurt://"):
|
if clean_url.begins_with("lw://"):
|
||||||
clean_url = clean_url.right(-7)
|
clean_url = clean_url.right(-7)
|
||||||
|
|
||||||
BrowserHistory.add_entry(clean_url, title, icon_url)
|
BrowserHistory.add_entry(clean_url, title, icon_url)
|
||||||
@@ -922,7 +922,7 @@ func get_startup_behavior() -> Dictionary:
|
|||||||
func _handle_startup_behavior():
|
func _handle_startup_behavior():
|
||||||
var args = OS.get_cmdline_args()
|
var args = OS.get_cmdline_args()
|
||||||
for arg in args:
|
for arg in args:
|
||||||
if arg.begins_with("gurt://"):
|
if arg.begins_with("lw://"):
|
||||||
print("Opening URL from command line: ", arg)
|
print("Opening URL from command line: ", arg)
|
||||||
_on_search_submitted(arg, true)
|
_on_search_submitted(arg, true)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ For production deployments, you can use the Gurted Certificate Authority to get
|
|||||||
|
|
||||||
3. **Follow the DNS challenge instructions:**
|
3. **Follow the DNS challenge instructions:**
|
||||||
When prompted, add the TXT record to your domain:
|
When prompted, add the TXT record to your domain:
|
||||||
- Go to gurt://dns.web (or your DNS server)
|
- Go to lw://dns.web (or your DNS server)
|
||||||
- Login and navigate to your domain
|
- Login and navigate to your domain
|
||||||
- Add a TXT record with:
|
- Add a TXT record with:
|
||||||
- Name: `_gurtca-challenge`
|
- Name: `_gurtca-challenge`
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ GURT networking extension for Godot.
|
|||||||
var client = GurtProtocolClient.new()
|
var client = GurtProtocolClient.new()
|
||||||
client.create_client(30) # 30s timeout
|
client.create_client(30) # 30s timeout
|
||||||
|
|
||||||
var response = client.request("gurt://127.0.0.1:4878", {"method": "GET"})
|
var response = client.request("lw://127.0.0.1:4878", {"method": "GET"})
|
||||||
|
|
||||||
client.disconnect() # cleanup
|
client.disconnect() # cleanup
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use crate::client::{Challenge, GurtCAClient};
|
|||||||
|
|
||||||
pub async fn complete_dns_challenge(challenge: &Challenge, client: &GurtCAClient) -> Result<()> {
|
pub async fn complete_dns_challenge(challenge: &Challenge, client: &GurtCAClient) -> Result<()> {
|
||||||
println!("Please add this TXT record to your domain:");
|
println!("Please add this TXT record to your domain:");
|
||||||
println!(" 1. Go to gurt://dns.web (or your DNS server)");
|
println!(" 1. Go to lw://dns.web (or your DNS server)");
|
||||||
println!(" 2. Login and navigate to your domain: {}", challenge.domain);
|
println!(" 2. Login and navigate to your domain: {}", challenge.domain);
|
||||||
println!(" 3. Add TXT record:");
|
println!(" 3. Add TXT record:");
|
||||||
println!(" Name: _gurtca-challenge");
|
println!(" Name: _gurtca-challenge");
|
||||||
@@ -30,7 +30,7 @@ async fn verify_dns_txt_record(domain: &str, expected_value: &str, client: &Gurt
|
|||||||
});
|
});
|
||||||
|
|
||||||
let response = client
|
let response = client
|
||||||
.post_json("gurt://dns.web/resolve-full", &request)
|
.post_json("lw://dns.web/resolve-full", &request)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if response.is_success() {
|
if response.is_success() {
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ impl GurtCAClient {
|
|||||||
|
|
||||||
async fn fetch_ca_via_http(&self) -> Result<String> {
|
async fn fetch_ca_via_http(&self) -> Result<String> {
|
||||||
let http_url = self.ca_url
|
let http_url = self.ca_url
|
||||||
.replace("gurt://", "http://")
|
.replace("lw://", "http://")
|
||||||
.replace(":8877", ":8876");
|
.replace(":8877", ":8876");
|
||||||
|
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ struct Cli {
|
|||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
command: Commands,
|
command: Commands,
|
||||||
|
|
||||||
#[arg(long, default_value = "gurt://dns.web")]
|
#[arg(long, default_value = "lw://dns.web")]
|
||||||
ca_url: String,
|
ca_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ edition = "2021"
|
|||||||
authors = ["FaceDev"]
|
authors = ["FaceDev"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/outpoot/gurted"
|
repository = "https://github.com/outpoot/gurted"
|
||||||
description = "Official GURT:// protocol implementation"
|
description = "Official lw:// protocol implementation"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "gurtlib"
|
name = "gurtlib"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ async fn main() -> Result<()> {
|
|||||||
Ok(GurtResponse::ok().with_string_body("Test endpoint working!"))
|
Ok(GurtResponse::ok().with_string_body("Test endpoint working!"))
|
||||||
});
|
});
|
||||||
|
|
||||||
println!("Starting GURT server on gurt://127.0.0.1:4878");
|
println!("Starting GURT server on lw://127.0.0.1:4878");
|
||||||
|
|
||||||
server.listen("127.0.0.1:4878").await
|
server.listen("127.0.0.1:4878").await
|
||||||
}
|
}
|
||||||
@@ -518,7 +518,7 @@ impl GurtClient {
|
|||||||
let parsed_url = Url::parse(url).map_err(|e| GurtError::invalid_message(format!("Invalid URL: {}", e)))?;
|
let parsed_url = Url::parse(url).map_err(|e| GurtError::invalid_message(format!("Invalid URL: {}", e)))?;
|
||||||
|
|
||||||
if parsed_url.scheme() != "gurt" {
|
if parsed_url.scheme() != "gurt" {
|
||||||
return Err(GurtError::invalid_message("URL must use gurt:// scheme"));
|
return Err(GurtError::invalid_message("URL must use lw:// scheme"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let host = parsed_url.host_str().ok_or_else(|| GurtError::invalid_message("URL must have a host"))?.to_string();
|
let host = parsed_url.host_str().ok_or_else(|| GurtError::invalid_message("URL must have a host"))?.to_string();
|
||||||
@@ -767,12 +767,12 @@ mod tests {
|
|||||||
async fn test_url_parsing() {
|
async fn test_url_parsing() {
|
||||||
let client = GurtClient::new();
|
let client = GurtClient::new();
|
||||||
|
|
||||||
let (host, port, path) = client.parse_gurt_url("gurt://example.com/test").unwrap();
|
let (host, port, path) = client.parse_gurt_url("lw://example.com/test").unwrap();
|
||||||
assert_eq!(host, "example.com");
|
assert_eq!(host, "example.com");
|
||||||
assert_eq!(port, DEFAULT_PORT);
|
assert_eq!(port, DEFAULT_PORT);
|
||||||
assert_eq!(path, "/test");
|
assert_eq!(path, "/test");
|
||||||
|
|
||||||
let (host, port, path) = client.parse_gurt_url("gurt://example.com:8080/api/v1").unwrap();
|
let (host, port, path) = client.parse_gurt_url("lw://example.com:8080/api/v1").unwrap();
|
||||||
assert_eq!(host, "example.com");
|
assert_eq!(host, "example.com");
|
||||||
assert_eq!(port, 8080);
|
assert_eq!(port, 8080);
|
||||||
assert_eq!(path, "/api/v1");
|
assert_eq!(path, "/api/v1");
|
||||||
|
|||||||
38
search-engine/Cargo.lock
generated
38
search-engine/Cargo.lock
generated
@@ -967,24 +967,6 @@ version = "0.3.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "gurt"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"base64 0.22.1",
|
|
||||||
"chrono",
|
|
||||||
"rustls 0.23.31",
|
|
||||||
"rustls-native-certs",
|
|
||||||
"rustls-pemfile 2.2.0",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"thiserror",
|
|
||||||
"tokio",
|
|
||||||
"tokio-rustls",
|
|
||||||
"tracing",
|
|
||||||
"url",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gurted-search-engine"
|
name = "gurted-search-engine"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -995,7 +977,7 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
"futures",
|
"futures",
|
||||||
"glob",
|
"glob",
|
||||||
"gurt",
|
"gurtlib",
|
||||||
"lol_html",
|
"lol_html",
|
||||||
"mime",
|
"mime",
|
||||||
"regex",
|
"regex",
|
||||||
@@ -1016,6 +998,24 @@ dependencies = [
|
|||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gurtlib"
|
||||||
|
version = "0.1.1"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
|
"chrono",
|
||||||
|
"rustls 0.23.31",
|
||||||
|
"rustls-native-certs",
|
||||||
|
"rustls-pemfile 2.2.0",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
"tokio-rustls",
|
||||||
|
"tracing",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.3.27"
|
version = "0.3.27"
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ impl Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn gurt_protocol_url(&self) -> String {
|
pub fn gurt_protocol_url(&self) -> String {
|
||||||
format!("gurt://{}:{}", self.server.address, self.server.port)
|
format!("lw://{}:{}", self.server.address, self.server.port)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_allowed_extension(&self, extension: &str) -> bool {
|
pub fn is_allowed_extension(&self, extension: &str) -> bool {
|
||||||
|
|||||||
@@ -418,7 +418,7 @@ impl DomainCrawler {
|
|||||||
let url_str = Self::normalize_url(absolute_url.to_string());
|
let url_str = Self::normalize_url(absolute_url.to_string());
|
||||||
|
|
||||||
// Only include GURT protocol URLs for the same domain
|
// Only include GURT protocol URLs for the same domain
|
||||||
if url_str.starts_with("gurt://") {
|
if url_str.starts_with("lw://") {
|
||||||
if let Ok(parsed) = Url::parse(&url_str) {
|
if let Ok(parsed) = Url::parse(&url_str) {
|
||||||
if let Some(host) = parsed.host_str() {
|
if let Some(host) = parsed.host_str() {
|
||||||
if let Ok(base_parsed) = Url::parse(base_url) {
|
if let Ok(base_parsed) = Url::parse(base_url) {
|
||||||
@@ -742,31 +742,31 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parse_clanker_rules_preserves_case_in_allowed_urls() {
|
fn parse_clanker_rules_preserves_case_in_allowed_urls() {
|
||||||
let content = "User-agent: TestBot\nAllow: /getpage?l=Fri,12Sep2025000605_ZzesV.txt\n";
|
let content = "User-agent: TestBot\nAllow: /getpage?l=Fri,12Sep2025000605_ZzesV.txt\n";
|
||||||
let result = DomainCrawler::parse_clanker_rules(content, "gurt://wi.ki", "TestBot")
|
let result = DomainCrawler::parse_clanker_rules(content, "lw://wi.ki", "TestBot")
|
||||||
.expect("expected allow list");
|
.expect("expected allow list");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
vec!["gurt://wi.ki/getpage?l=Fri,12Sep2025000605_ZzesV.txt".to_string()]
|
vec!["lw://wi.ki/getpage?l=Fri,12Sep2025000605_ZzesV.txt".to_string()]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_clanker_rules_handles_case_insensitive_directives() {
|
fn parse_clanker_rules_handles_case_insensitive_directives() {
|
||||||
let content = "user-Agent: AnotherBot\nAlLoW: /MiXeD/Path.HTML\n";
|
let content = "user-Agent: AnotherBot\nAlLoW: /MiXeD/Path.HTML\n";
|
||||||
let result = DomainCrawler::parse_clanker_rules(content, "gurt://example", "AnotherBot")
|
let result = DomainCrawler::parse_clanker_rules(content, "lw://example", "AnotherBot")
|
||||||
.expect("expected allow list");
|
.expect("expected allow list");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
vec!["gurt://example/MiXeD/Path.HTML".to_string()]
|
vec!["lw://example/MiXeD/Path.HTML".to_string()]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_clanker_rules_respects_disallow_all() {
|
fn parse_clanker_rules_respects_disallow_all() {
|
||||||
let content = "User-agent: Bot\nDisallow: /\n";
|
let content = "User-agent: Bot\nDisallow: /\n";
|
||||||
let result = DomainCrawler::parse_clanker_rules(content, "gurt://example", "Bot");
|
let result = DomainCrawler::parse_clanker_rules(content, "lw://example", "Bot");
|
||||||
|
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ impl Domain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn gurt_url(&self) -> String {
|
pub fn gurt_url(&self) -> String {
|
||||||
format!("gurt://{}.{}", self.name, self.tld)
|
format!("lw://{}.{}", self.name, self.tld)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<link rel="apple-touch-icon" href="/favicon.ico" />
|
<link rel="apple-touch-icon" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>Gurted</title>
|
<title>Gurted</title>
|
||||||
<meta name="description" content="Gurted introduces a new web ecosystem with the gurt:// protocol, custom browser Flumi, and revolutionary HTML/CSS/Lua standards." />
|
<meta name="description" content="Gurted introduces a new web ecosystem with the lw:// protocol, custom browser Flumi, and revolutionary HTML/CSS/Lua standards." />
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
</head>
|
</head>
|
||||||
<body data-sveltekit-preload-data="hover" class="dark bg-background text-foreground">
|
<body data-sveltekit-preload-data="hover" class="dark bg-background text-foreground">
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
<title>Gurted</title>
|
<title>Gurted</title>
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
content="A web ecosystem with the gurt:// protocol, custom browser Flumi, DNS system, and modern HTML/CSS/Lua standards."
|
content="A web ecosystem with the lw:// protocol, custom browser Flumi, DNS system, and modern HTML/CSS/Lua standards."
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" rel="stylesheet" />
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" rel="stylesheet" />
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
</h1>
|
</h1>
|
||||||
<p class="text-muted-foreground mx-auto mb-4 max-w-3xl text-xl md:text-2xl">
|
<p class="text-muted-foreground mx-auto mb-4 max-w-3xl text-xl md:text-2xl">
|
||||||
A web ecosystem introducing the <code
|
A web ecosystem introducing the <code
|
||||||
class="bg-muted text-primary rounded px-2 py-1 font-mono">gurt://</code
|
class="bg-muted text-primary rounded px-2 py-1 font-mono">lw://</code
|
||||||
> protocol
|
> protocol
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -246,7 +246,7 @@
|
|||||||
class="language-html"
|
class="language-html"
|
||||||
>{`<head>
|
>{`<head>
|
||||||
<title>My Gurt Site</title>
|
<title>My Gurt Site</title>
|
||||||
<icon src="gurt://example.real/icon.png">
|
<icon src="lw://example.real/icon.png">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -77,7 +77,7 @@
|
|||||||
-- no ai slop btw
|
-- no ai slop btw
|
||||||
downloadPeakBtn:on('click', function()
|
downloadPeakBtn:on('click', function()
|
||||||
-- i expect you to host this yourself
|
-- i expect you to host this yourself
|
||||||
local downloadId = gurt.download("gurt://127.0.0.1", "peakshit.iso")
|
local downloadId = gurt.download("lw://127.0.0.1", "peakshit.iso")
|
||||||
addLog(`started your peak download {downloadId}`)
|
addLog(`started your peak download {downloadId}`)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ gurt.select("#download-shit"):on("click", function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
gurt.select("#download-peak"):on("click", function()
|
gurt.select("#download-peak"):on("click", function()
|
||||||
local downloadId = gurt.download("gurt://127.0.0.1", "peakshit.iso")
|
local downloadId = gurt.download("lw://127.0.0.1", "peakshit.iso")
|
||||||
end)
|
end)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user