News from tauri
This commit is contained in:
Generated
+329
-2
@@ -581,6 +581,12 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527"
|
||||
|
||||
[[package]]
|
||||
name = "chacha20"
|
||||
version = "0.10.2"
|
||||
@@ -590,6 +596,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cpufeatures 0.3.1",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1165,6 +1172,8 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"chacha20poly1305",
|
||||
"cpal",
|
||||
"quick-xml 0.37.5",
|
||||
"reqwest 0.12.28",
|
||||
"ringbuf",
|
||||
"rubato",
|
||||
"serde",
|
||||
@@ -1172,6 +1181,16 @@ dependencies = [
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-opener",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1565,8 +1584,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1588,9 +1609,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"r-efi 6.0.0",
|
||||
"rand_core",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1741,6 +1764,25 @@ dependencies = [
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.4.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
"fnv",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"http",
|
||||
"indexmap 2.14.0",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
@@ -1845,6 +1887,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
@@ -1855,6 +1898,22 @@ dependencies = [
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.27.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f"
|
||||
dependencies = [
|
||||
"http",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"rustls",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.20"
|
||||
@@ -2419,6 +2478,12 @@ version = "0.4.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
|
||||
|
||||
[[package]]
|
||||
name = "lru-slab"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
||||
|
||||
[[package]]
|
||||
name = "mach2"
|
||||
version = "0.6.0"
|
||||
@@ -3076,7 +3141,7 @@ checksum = "7da1d65da6dd5d1e44199ac0f58712d241c0f439f80adea8924d832384087f85"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"indexmap 2.14.0",
|
||||
"quick-xml",
|
||||
"quick-xml 0.41.0",
|
||||
"serde",
|
||||
"time",
|
||||
]
|
||||
@@ -3254,6 +3319,15 @@ dependencies = [
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.37.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.41.0"
|
||||
@@ -3263,6 +3337,62 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn"
|
||||
version = "0.11.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"cfg_aliases",
|
||||
"pin-project-lite",
|
||||
"quinn-proto",
|
||||
"quinn-udp",
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
"socket2",
|
||||
"thiserror 2.0.20",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn-proto"
|
||||
version = "0.11.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04759210543be93709136e28212294a659ef5001836ff4eab4d663e4529bba83"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"getrandom 0.4.3",
|
||||
"lru-slab",
|
||||
"rand",
|
||||
"rand_pcg",
|
||||
"ring",
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"slab",
|
||||
"thiserror 2.0.20",
|
||||
"tinyvec",
|
||||
"tracing",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn-udp"
|
||||
version = "0.5.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694"
|
||||
dependencies = [
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"socket2",
|
||||
"tracing",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.47"
|
||||
@@ -3284,12 +3414,32 @@ version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
|
||||
dependencies = [
|
||||
"chacha20",
|
||||
"getrandom 0.4.3",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
|
||||
|
||||
[[package]]
|
||||
name = "rand_pcg"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "raw-window-handle"
|
||||
version = "0.6.2"
|
||||
@@ -3374,6 +3524,47 @@ version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-rustls",
|
||||
"hyper-util",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.13.4"
|
||||
@@ -3408,6 +3599,20 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"getrandom 0.2.17",
|
||||
"libc",
|
||||
"untrusted",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ringbuf"
|
||||
version = "0.5.1"
|
||||
@@ -3477,12 +3682,53 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96"
|
||||
dependencies = [
|
||||
"web-time",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
@@ -3673,6 +3919,18 @@ dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_urlencoded"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "3.22.0"
|
||||
@@ -3910,6 +4168,12 @@ version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "swift-rs"
|
||||
version = "1.0.7"
|
||||
@@ -4092,7 +4356,7 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"plist",
|
||||
"raw-window-handle",
|
||||
"reqwest",
|
||||
"reqwest 0.13.4",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
@@ -4441,9 +4705,31 @@ dependencies = [
|
||||
"mio",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.26.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.19"
|
||||
@@ -4453,6 +4739,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"libc",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
]
|
||||
@@ -4783,6 +5070,12 @@ dependencies = [
|
||||
"ctutils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.8"
|
||||
@@ -4981,6 +5274,16 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-time"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web_atoms"
|
||||
version = "0.2.6"
|
||||
@@ -5037,6 +5340,15 @@ dependencies = [
|
||||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webview2-com"
|
||||
version = "0.38.2"
|
||||
@@ -5318,6 +5630,15 @@ dependencies = [
|
||||
"windows-targets 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
@@ -5709,6 +6030,12 @@ dependencies = [
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
version = "0.2.5"
|
||||
|
||||
@@ -26,4 +26,7 @@ cpal = { version = "0.18.2", features = ["pipewire", "pulseaudio"] }
|
||||
ringbuf = "0.5.1"
|
||||
rubato = "5.0.0"
|
||||
chacha20poly1305 = "0.11.0"
|
||||
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "charset", "http2"] }
|
||||
tokio = { version = "1", features = ["time", "macros"] }
|
||||
quick-xml = "0.37"
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod accounts;
|
||||
pub mod audio;
|
||||
pub mod config;
|
||||
pub mod news;
|
||||
pub mod server_list;
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
use quick_xml::events::Event;
|
||||
use quick_xml::Reader;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Serialize, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NewsItem {
|
||||
pub title: String,
|
||||
pub link: String,
|
||||
pub source: String,
|
||||
pub published_at: i64,
|
||||
}
|
||||
|
||||
struct Feed {
|
||||
url: &'static str,
|
||||
source: &'static str,
|
||||
}
|
||||
|
||||
const FEEDS: &[Feed] = &[
|
||||
Feed {
|
||||
url: "https://feeds.feedburner.com/TheHackersNews",
|
||||
source: "The Hacker News",
|
||||
},
|
||||
Feed {
|
||||
url: "https://www.cisa.gov/cybersecurity-advisories/all.xml",
|
||||
source: "CISA",
|
||||
},
|
||||
Feed {
|
||||
url: "https://krebsonsecurity.com/feed/",
|
||||
source: "Krebs on Security",
|
||||
},
|
||||
Feed {
|
||||
url: "https://www.bleepingcomputer.com/feed/",
|
||||
source: "BleepingComputer",
|
||||
},
|
||||
];
|
||||
|
||||
const MAX_ITEMS: usize = 15;
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct RssItem {
|
||||
title: String,
|
||||
link: String,
|
||||
pub_date: String,
|
||||
}
|
||||
|
||||
fn field_map() -> &'static [(&'static [u8], &'static str)] {
|
||||
&[
|
||||
(b"title", "title"),
|
||||
(b"link", "link"),
|
||||
(b"pubDate", "pubDate"),
|
||||
]
|
||||
}
|
||||
|
||||
fn parse_feed(xml: &str) -> Vec<RssItem> {
|
||||
let mut reader = Reader::from_str(xml);
|
||||
reader.config_mut().trim_text(false);
|
||||
|
||||
let mut items = Vec::new();
|
||||
let mut current: Option<RssItem> = None;
|
||||
// Stack of open element names (lowercased) while inside an <item>.
|
||||
let mut stack: Vec<Vec<u8>> = Vec::new();
|
||||
let mut buf = Vec::new();
|
||||
|
||||
loop {
|
||||
match reader.read_event_into(&mut buf) {
|
||||
Ok(Event::Start(e)) => {
|
||||
let name = e.local_name().as_ref().to_ascii_lowercase();
|
||||
if name == b"item" {
|
||||
current = Some(RssItem::default());
|
||||
}
|
||||
stack.push(name);
|
||||
}
|
||||
Ok(Event::End(e)) => {
|
||||
let name = e.local_name().as_ref().to_ascii_lowercase();
|
||||
if name == b"item" {
|
||||
if let Some(item) = current.take() {
|
||||
if !item.title.is_empty() && !item.link.is_empty() {
|
||||
items.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
stack.pop();
|
||||
}
|
||||
Ok(Event::Text(e)) => {
|
||||
if let Some(item) = current.as_mut() {
|
||||
if let Ok(text) = std::str::from_utf8(e.as_ref()) {
|
||||
for (tag, field) in field_map() {
|
||||
if stack.last().map(|s| s.as_slice()) == Some(*tag) {
|
||||
match *field {
|
||||
"title" => item.title.push_str(text),
|
||||
"link" => item.link.push_str(text),
|
||||
"pubDate" => item.pub_date.push_str(text),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Event::Eof) | Err(_) => break,
|
||||
_ => {}
|
||||
}
|
||||
buf.clear();
|
||||
}
|
||||
|
||||
for item in items.iter_mut() {
|
||||
item.title = item.title.trim().to_string();
|
||||
item.link = item.link.trim().to_string();
|
||||
item.pub_date = item.pub_date.trim().to_string();
|
||||
}
|
||||
|
||||
items
|
||||
}
|
||||
|
||||
/// Best-effort RFC 822 RSS date parsing; returns 0 on any failure.
|
||||
fn parse_date(value: &str) -> i64 {
|
||||
if value.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
let value = value.split_once(',').map(|(_, rest)| rest).unwrap_or(value);
|
||||
let parts: Vec<&str> = value.split_whitespace().collect();
|
||||
if parts.len() < 5 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let day: i64 = parts[0].parse().unwrap_or(0);
|
||||
let month = match parts[1] {
|
||||
"Jan" => 1,
|
||||
"Feb" => 2,
|
||||
"Mar" => 3,
|
||||
"Apr" => 4,
|
||||
"May" => 5,
|
||||
"Jun" => 6,
|
||||
"Jul" => 7,
|
||||
"Aug" => 8,
|
||||
"Sep" => 9,
|
||||
"Oct" => 10,
|
||||
"Nov" => 11,
|
||||
"Dec" => 12,
|
||||
_ => 0,
|
||||
};
|
||||
let year: i64 = parts[2].parse().unwrap_or(0);
|
||||
let hm: Vec<&str> = parts[3].split(':').collect();
|
||||
if hm.len() < 3 || year == 0 || month == 0 || day == 0 {
|
||||
return 0;
|
||||
}
|
||||
let hour: i64 = hm[0].parse().unwrap_or(0);
|
||||
let minute: i64 = hm[1].parse().unwrap_or(0);
|
||||
let second: i64 = hm[2].parse().unwrap_or(0);
|
||||
|
||||
let days_before_month = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
|
||||
let leap = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
|
||||
let yday = days_before_month[(month - 1) as usize] + day + if leap && month > 2 { 1 } else { 0 };
|
||||
|
||||
let days = (year - 1970) * 365 + (year - 1969) / 4 + yday - 1;
|
||||
days * 86_400 + hour * 3_600 + minute * 60 + second
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_cyber_news() -> Result<Vec<NewsItem>, String> {
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(std::time::Duration::from_secs(10))
|
||||
.build()
|
||||
.map_err(|e| format!("failed to build http client: {e}"))?;
|
||||
|
||||
let mut all_items: Vec<NewsItem> = Vec::new();
|
||||
|
||||
for feed in FEEDS {
|
||||
let resp = client
|
||||
.get(feed.url)
|
||||
.header("User-Agent", "Mozilla/5.0")
|
||||
.header(
|
||||
"Accept",
|
||||
"application/rss+xml, application/xml;q=0.9, text/xml;q=0.8, */*;q=0.7",
|
||||
)
|
||||
.send()
|
||||
.await;
|
||||
|
||||
let Ok(resp) = resp else { continue };
|
||||
if !resp.status().is_success() {
|
||||
continue;
|
||||
}
|
||||
let Ok(body) = resp.text().await else {
|
||||
continue;
|
||||
};
|
||||
|
||||
all_items.extend(
|
||||
parse_feed(&body)
|
||||
.into_iter()
|
||||
.map(|item| NewsItem {
|
||||
title: item.title,
|
||||
link: item.link,
|
||||
source: feed.source.to_string(),
|
||||
published_at: parse_date(&item.pub_date),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if all_items.is_empty() {
|
||||
return Err("No cyber news available right now.".to_string());
|
||||
}
|
||||
|
||||
all_items.sort_by(|a, b| b.published_at.cmp(&a.published_at));
|
||||
|
||||
let mut seen = std::collections::HashSet::new();
|
||||
let mut deduped = Vec::new();
|
||||
for item in all_items {
|
||||
if seen.insert(item.link.clone()) {
|
||||
deduped.push(item);
|
||||
if deduped.len() >= MAX_ITEMS {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(deduped)
|
||||
}
|
||||
@@ -25,6 +25,7 @@ pub fn run() {
|
||||
commands::config::save_config,
|
||||
commands::config::get_config,
|
||||
commands::config::update_backend_config,
|
||||
commands::news::get_cyber_news,
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
|
||||
+2
-96
@@ -1,4 +1,4 @@
|
||||
import axios from "axios";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
|
||||
export type NewsItem = {
|
||||
title: string;
|
||||
@@ -7,100 +7,6 @@ export type NewsItem = {
|
||||
publishedAt: number;
|
||||
};
|
||||
|
||||
const FEEDS: { href: string; source: string }[] = [
|
||||
{
|
||||
href: "/news/thn",
|
||||
source: "The Hacker News",
|
||||
},
|
||||
{
|
||||
href: "/news/cisa",
|
||||
source: "CISA",
|
||||
},
|
||||
{
|
||||
href: "/news/krebs",
|
||||
source: "Krebs on Security",
|
||||
},
|
||||
{
|
||||
href: "/news/bc",
|
||||
source: "BleepingComputer",
|
||||
},
|
||||
];
|
||||
|
||||
const MAX_ITEMS = 15;
|
||||
const REQUEST_TIMEOUT_MS = 10_000;
|
||||
|
||||
function parseDate(value: string | null | undefined): number {
|
||||
if (!value) return 0;
|
||||
|
||||
const ms = Date.parse(value);
|
||||
return Number.isNaN(ms) ? 0 : ms;
|
||||
}
|
||||
|
||||
function textOf(el: Element | undefined | null, tag: string): string {
|
||||
return el?.getElementsByTagName(tag)[0]?.textContent?.trim() ?? "";
|
||||
}
|
||||
|
||||
function dateOf(el: Element): number {
|
||||
const pubDate = textOf(el, "pubDate");
|
||||
if (pubDate) return parseDate(pubDate);
|
||||
|
||||
const dc = el.getElementsByTagName("dc:date")[0]?.textContent?.trim();
|
||||
return parseDate(dc);
|
||||
}
|
||||
|
||||
function parseFeed(xml: string, source: string): NewsItem[] {
|
||||
const doc = new DOMParser().parseFromString(xml, "text/xml");
|
||||
|
||||
if (doc.querySelector("parsererror")) return [];
|
||||
|
||||
const items: NewsItem[] = [];
|
||||
|
||||
doc.querySelectorAll("item").forEach((item) => {
|
||||
const title = textOf(item, "title");
|
||||
const link = textOf(item, "link");
|
||||
if (!title || !link) return;
|
||||
|
||||
items.push({
|
||||
title,
|
||||
link,
|
||||
source,
|
||||
publishedAt: dateOf(item),
|
||||
});
|
||||
});
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
export async function getCyberNews(): Promise<NewsItem[]> {
|
||||
const results = await Promise.allSettled(
|
||||
FEEDS.map(({ href, source }) =>
|
||||
axios
|
||||
.get<string>(href, { timeout: REQUEST_TIMEOUT_MS })
|
||||
.then((res) => parseFeed(res.data, source)),
|
||||
),
|
||||
);
|
||||
|
||||
const items: NewsItem[] = [];
|
||||
|
||||
results.forEach((result) => {
|
||||
if (result.status === "fulfilled") {
|
||||
items.push(...result.value);
|
||||
}
|
||||
});
|
||||
|
||||
if (items.length === 0) {
|
||||
throw new Error("No cyber news available right now.");
|
||||
}
|
||||
|
||||
const seen = new Set<string>();
|
||||
const deduped = items
|
||||
.sort((a, b) => b.publishedAt - a.publishedAt)
|
||||
.filter((item) => {
|
||||
const key = item.link;
|
||||
if (seen.has(key)) return false;
|
||||
seen.add(key);
|
||||
return true;
|
||||
});
|
||||
|
||||
return deduped.slice(0, MAX_ITEMS);
|
||||
return await invoke<NewsItem[]>("get_cyber_news");
|
||||
}
|
||||
|
||||
@@ -2,46 +2,6 @@ import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import path from "path";
|
||||
import type { ProxyOptions } from "vite";
|
||||
|
||||
const newsProxy: Record<string, ProxyOptions> = {
|
||||
"/news/thn": {
|
||||
target: "https://feeds.feedburner.com",
|
||||
changeOrigin: true,
|
||||
rewrite: (p) => p.replace(/^\/news\/thn/, "/TheHackersNews"),
|
||||
headers: {
|
||||
"User-Agent": "Mozilla/5.0",
|
||||
},
|
||||
},
|
||||
"/news/krebs": {
|
||||
target: "https://krebsonsecurity.com",
|
||||
changeOrigin: true,
|
||||
rewrite: (p) => p.replace(/^\/news\/krebs/, "/feed/"),
|
||||
headers: {
|
||||
"User-Agent": "Mozilla/5.0",
|
||||
},
|
||||
},
|
||||
"/news/cisa": {
|
||||
target: "https://www.cisa.gov",
|
||||
changeOrigin: true,
|
||||
rewrite: (p) =>
|
||||
p.replace(/^\/news\/cisa/, "/cybersecurity-advisories/all.xml"),
|
||||
headers: {
|
||||
"User-Agent": "Mozilla/5.0",
|
||||
},
|
||||
},
|
||||
"/news/bc": {
|
||||
target: "https://www.bleepingcomputer.com",
|
||||
changeOrigin: true,
|
||||
rewrite: (p) => p.replace(/^\/news\/bc/, "/feed/"),
|
||||
headers: {
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120 Safari/537.36",
|
||||
Accept:
|
||||
"application/rss+xml, application/xml;q=0.9, text/xml;q=0.8, */*;q=0.7",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const host = process.env.TAURI_DEV_HOST;
|
||||
|
||||
@@ -64,7 +24,6 @@ export default defineConfig(async () => ({
|
||||
port: 1420,
|
||||
strictPort: true,
|
||||
host: host || false,
|
||||
proxy: newsProxy,
|
||||
hmr: host
|
||||
? {
|
||||
protocol: "ws",
|
||||
|
||||
Reference in New Issue
Block a user