remove ShowTorrentPage from webconsole

This commit is contained in:
orignal committed 2026-10-13 21:45:47 -04:00
1 parent a2cf28cb04
commit 9cd8036b3b
4 files changed
-486

No files matched your search

-105
View File
@@ -1,105 +0,0 @@
class TorrentClient {
constructor(host = '127.0.0.1', port = 9191, path = 'mytorrents') {
this.host = host;
this.port = port;
this.path = path;
}
setConnection(host, port, path) {
this.host = host;
this.port = port;
this.path = path;
}
getEndpoint() {
return `http://${this.host}:${this.port}${this.path}`;
}
getStatusString(status) {
switch (status) {
case 4: return 'DOWNLOADING';
case 6: return 'SEEDING';
case 0: return 'STOPPED';
case 2: return 'CHECKING';
default: return 'QUEUED';
}
}
formatBytes(bytesPerSec) {
if (!bytesPerSec) return '0 B/s';
if (bytesPerSec > 1024 * 1024) {
return (bytesPerSec / (1024 * 1024)).toFixed(2) + ' MB/s';
}
return (bytesPerSec / 1024).toFixed(2) + ' KB/s';
}
async addTorrentByMagnet(magnet)
{
// TODO: check link
const magnetRegex = /^magnet:\?xt=urn:btih:[a-f0-9]{40}(&.+)?$/i;
if(!magnetRegex.test(magnet)) return false;
const res = await fetch(this.getEndpoint(), {
method: "POST",
body: JSON.stringify({ 'method': 'torrent-add', 'arguments': { 'filename': magnet } }),
headers: { 'Content-Type': 'application/json' }
});
return res.json();
}
async addTorrent(b64metainfo) {
const res = await fetch(this.getEndpoint(), {
method: "POST",
body: JSON.stringify({ 'method': 'torrent-add', 'arguments': { 'metainfo': b64metainfo } }),
headers: { 'Content-Type': 'application/json' }
});
return res.json();
}
async removeTorrent(id) {
const res = await fetch(this.getEndpoint(), {
method: "POST",
body: JSON.stringify({ 'method': 'torrent-remove', 'arguments': { 'ids': [id], 'delete-local-data': true } }),
headers: { 'Content-Type': 'application/json' }
});
return res.json();
}
async getTorrents() {
const res = await fetch(this.getEndpoint(), {
method: "POST",
body: JSON.stringify({
'method': 'torrent-get',
'arguments': { "fields": ["id", "name", "status", "rateDownload", "rateUpload", "totalSize", "percentDone", "files", "peers", "trackers", "trackerStats","hashString"] }
}),
headers: { 'Content-Type': 'application/json' }
});
return res.json();
}
async stopTorrent(ids = []) {
const res = await fetch(this.getEndpoint(), {
method: "POST",
body: JSON.stringify({
'method': 'torrent-stop',
'arguments': ids.length ? { ids } : {},
'tag': 666
}),
headers: { 'Content-Type': 'application/json' }
});
return res.json();
}
async startTorrent(ids = []) {
const res = await fetch(this.getEndpoint(), {
method: "POST",
body: JSON.stringify({
'method': 'torrent-start',
'arguments': ids.length ? { ids } : {},
'tag': 666
}),
headers: { 'Content-Type': 'application/json' }
});
return res.json();
}
}
//window.tjs = new TorrentClient();
-312
View File
@@ -1,312 +0,0 @@
<style>
/*todo: more 2000 design*/
#torrent-ui button {
border-radius: 6.66px;
padding: 0.1vw;
margin: 2px;
text-decoration: underline;
color: black;
background: aquamarine;
text-transform: uppercase;
}
#torrent-ui button:hover {
cursor:pointer;
background-color: yellow;
transition: background-color 0.5s ease 0.1s;
}
#torrent-ui {
display: flex;
flex-direction: column;
gap: 2.5px;
}
#torrents li {
border-bottom: 2px solid #333333;
}
</style>
<fieldset>
<legend>Torrent Client</legend>
<div id="torrent-ui">
<label for='torrent_manet_url'>Your a torrent magnet link (only magnet): <input id='torrent_manet_url' type='text' id='torrent_manet_url'/><button id='btn_magnet'>download</button>
<h3 id="connection_status">_</h3>
<input type="file" id="torrentf" accept=".torrent" style="display: none" />
<button onclick="document.getElementById('torrentf').click()">
Add torrent
</button>
<button onclick="stopAll()">
stopAll
</button>
<button onclick="startAll()">
startAll
</button>
<ul id="torrents"></ul>
</div>
</fieldset>
<div id="torrent_details_modal" style="display: none">
<h3 id="modal_torrent_title"></h3>
<p style="color:#303030" id="modal_torrent_hash"></p>
<h4>Files:</h4>
<ul id="modal_files_list"></ul>
<h4>Peers (<span id="peer_count">0</span>):</h4>
<ul id="modal_peers_list"></ul>
<h4>Trackers (<span id="trackers_count">0</span>):</h4>
<ul id="modal_trackers_list"></ul>
<h4>trackerStats:</h4>
<pre id="modal_tracker_stats"></pre>
<button onclick="closeDetailsModal()">Close</button>
</div>
<script>
let currentTorrentsData = [];
async function stopAll()
{
const conf = confirm("Are sure to stop all torrents?");
// console.log(conf)
if(conf && window.tjs)
{
const r = await window.tjs.getTorrents()
//console.log(`torrents: ${r}`)
if (!r?.result?.torrents) return;
r.arguments.torrents.forEach(async (t) => {
console.log(`stop torrent => ${t.id}`)
await window.tjs.stopTorrent([t.id]);
});
}
//if (window.tjs && conf) await window.tjs.stopTorrent();
}
async function startAll()
{
const conf = confirm("Are sure to start all torrents?");
if(conf && window.tjs)
{
const r = await window.tjs.getTorrents()
//console.log(`torrents: ${r}`)
if (!r?.result?.torrents) return;
r.arguments.torrents.forEach( async (t) =>{
console.log(`start torrent => ${t.id}`)
await window.tjs.startTorrent([t.id]);
});
}
//if (window.tjs && conf) await window.tjs.startTorrent();
}
setInterval(async () => {
const statusIndicator = document.getElementById("connection_status");
if (!window.tjs)
return (statusIndicator.textContent = "STATUS: TJS_NOT_LOADED");
try {
const r = await window.tjs.getTorrents();
console.log(r)
if (!r?.result == "success")
return (statusIndicator.textContent = "STATUS: INVALID_RESPONSE");
statusIndicator.textContent = "STATUS: Connected";
statusIndicator.style.color = "rgb(77, 91, 83)";
const torrents = document.getElementById("torrents");
currentTorrentsData = r.arguments.torrents;
if (currentTorrentsData.length === 0) {
torrents.innerHTML = "<li>No downloads</li>";
return;
}
torrents.innerHTML = "";
currentTorrentsData.forEach((el) => {
const percent = el.percentDone
? (el.percentDone * 100).toFixed(2)
: "0.00";
const speed = window.tjs.formatBytes
? window.tjs.formatBytes(el.rateDownload)
: `${el.rateDownload} B/s`;
const speedUp = window.tjs.formatBytes
? window.tjs.formatBytes(el.rateUpload)
: `${el.rateUpload} B/s`;
const status = window.tjs.getStatusString
? window.tjs.getStatusString(el.status)
: el.status;
const totalSize = el.totalSize || 0; // bytes
const ETA = el.rateDownload
? (
(totalSize * (1 - el.percentDone)) /
el.rateDownload /
60
).toFixed(1) + " min"
: "-";
const name = el.name || "|unknown name, maybe a bad torrent|";
const li = document.createElement("li");
const span = document.createElement("span");
span.textContent = `${name} — ${percent}% [${status}] (⬇ ${speed} | ⬆ ${speedUp}) ---- size = ${(totalSize / 1024 / 1024).toFixed(2)}mb; ETA: ${ETA} `;
li.appendChild(span);
const butDetails = document.createElement("button");
butDetails.textContent = "Details";
butDetails.onclick = () => openTorrentDetails(el.id);
li.appendChild(butDetails);
const butStop = document.createElement("button");
butStop.textContent = "Stop";
butStop.onclick = async () => {
if (window.tjs) await window.tjs.stopTorrent([el.id]);
};
li.appendChild(butStop);
const butStart = document.createElement("button");
butStart.textContent = "Start";
butStart.onclick = async () => {
if (window.tjs) await window.tjs.startTorrent([el.id]);
};
li.appendChild(butStart);
const butRemove = document.createElement("button");
butRemove.textContent = "✕";
butRemove.onclick = () => removeTorrent(el.id);
li.appendChild(butRemove);
torrents.appendChild(li);
});
} catch (err) {
console.log(err)
statusIndicator.textContent =
"STATUS: ERROR_WITH_DATA (f12, check your console and write issue)";
statusIndicator.style.color = "#ff3333";
console.log(err)
}
}, 1000);
document.getElementById("torrentf").onchange = (e) => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = async () => {
const bytes = new Uint8Array(reader.result);
let binary = "";
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
const b64 = btoa(binary);
if (window.tjs) await window.tjs.addTorrent(b64);
e.target.value = "";
};
reader.readAsArrayBuffer(file);
};
window.removeTorrent = async (id) => {
const confirmed = confirm(`Drop the torrent with id ${id}?`);
if (confirmed && window.tjs) await window.tjs.removeTorrent(Number(id));
};
window.openTorrentDetails = (id) => {
const torrent = currentTorrentsData.find(
(t) => String(t.id) === String(id),
);
if (!torrent) return;
document.getElementById("modal_torrent_title").textContent =
`DETAILS: ${torrent.name} (#${torrent.id})`;
document.getElementById("modal_torrent_hash").textContent =
`InfoHash: ${torrent.hashString}`;
const filesList = document.getElementById("modal_files_list");
filesList.innerHTML = "";
if (torrent.files?.length) {
torrent.files.forEach((f) => {
const li = document.createElement("li");
const completedMB = (f.bytesCompleted / 1048576).toFixed(2);
const totalMB = (f.length / 1048576).toFixed(2);
const percent = ((f.bytesCompleted / f.length) * 100 || 0).toFixed(1);
li.textContent = `${f.name} — ${completedMB} / ${totalMB} MB (${percent}%)`;
filesList.appendChild(li);
});
} else {
const li = document.createElement("li");
li.textContent = "NO_FILES";
filesList.appendChild(li);
}
const peersList = document.getElementById("modal_peers_list");
const trackersList = document.getElementById("modal_trackers_list");
document.getElementById("peer_count").textContent = torrent.peers
? torrent.peers.length
: 0;
peersList.innerHTML = "";
if (torrent.peers?.length) {
peersList.append(
...torrent.peers.map((p) => {
const li = document.createElement("li");
const downSpeed = (p.rateToClient / 1024).toFixed(2);
const upSpeed = (p.rateToPeer / 1024).toFixed(2);
const client = p.clientName || "Unk";
li.textContent = `${p.address} (${client}) — ${downSpeed} KB/s — ${upSpeed} KB/s`;
return li;
}),
);
} else {
const li = document.createElement("li");
li.textContent = "NO_PEERS";
peersList.appendChild(li);
}
trackersList.innerHTML = "";
if (torrent.trackers?.length) {
trackersList.append(
...torrent.trackers.map((p) => {
const li = document.createElement("li");
li.textContent = `[id:${p.id}] announce: ${p.announce} ; tier - ${p.tier}`;
return li;
}),
);
} else {
const li = document.createElement("li");
li.textContent = "NO_TRACKERS";
trackersList.appendChild(li);
}
document.getElementById("trackers_count").textContent = torrent.trackers
? torrent.trackers.length
: 0;
const modal_TrackerStats = document.getElementById("modal_tracker_stats");
const tstats = torrent.trackerStats;
/*
To convert a Unix timestamp to a human-readable date in JavaScript, multiply the timestamp by 1000 and pass it into the new Date() constructor. JavaScript requires milliseconds, while standard Unix timestamps are measured in second
(so, for js we need ms)
*/
modal_TrackerStats.textContent = tstats
? tstats
.map(
(tr) => {
const leachers = tr.leecherCount >= 0 ? tr.leecherCount : '?'; // TODO: why so big value
return `
Host: ${tr.host}
Announce: ${tr.announce}
Status: ${tr.lastAnnounceResult} (${tr.lastAnnounceSucceeded ? "OK" : "Fail"})
Peers: ${tr.lastAnnouncePeerCount} | Seeders: ${tr.seederCount} | Leechers: ${leachers}
Last Announce: ${new Date(tr.lastAnnounceTime * 1000).toLocaleString()}
Next Announce: ${new Date(tr.nextAnnounceTime * 1000).toLocaleString()}
`}).join("\n" + "-".repeat(40)) : "-";
document.getElementById("torrent_details_modal").style.display = "block";
};
window.closeDetailsModal = () => {
document.getElementById("torrent_details_modal").style.display = "none";
};
const btn_magnet = document.getElementById('btn_magnet')
btn_magnet.onclick = async () => {
const r = await window.tjs.addTorrentByMagnet(document.getElementById('torrent_manet_url').value)
if(!r) alert("Not correct magnet link");
}
</script>
-68
View File
@@ -96,7 +96,6 @@ namespace http {
const char HTTP_COMMAND_RELOAD_CSS[] = "reload_css";
const char HTTP_COMMAND_EXPIRELEASE[] = "expirelease";
const char OPENJS_TORRENT_CLIENT_PAGE[] = "torrent_client_open";
static std::string ConvertTime (uint64_t time)
{
struct tm caltime;
@@ -1267,71 +1266,6 @@ namespace http {
#endif
}
static void ShowTorrentsPage (std::stringstream& s, std::map<std::string, std::string>& params)
{
#ifndef NO_TORRENTS
bool js; i2p::config::GetOption("http.javascript", js);
if (!js)
{
s << "<h1 style='color:red'>Javascript is off</h1>\r\n";
return;
}
std::string tunnelname = params["tunnelname"];
auto findTunnelEndpoint = [](const std::string& tunnelname) -> std::pair<uint16_t, std::string>
{
LogPrint(eLogDebug,"HTTPServer: GetTorrentsRPC");
for (auto& [port, server] : i2p::client::context.GetTorrentsRPCServers())
{
if (!server)
{
LogPrint(eLogError,"HTTPServer: GetTorrentsRPCServers return nothing");
continue;
}
for (const auto& [rpcPath, weakTunnel] : server->GetTunnels())
{
if (auto tunnel = weakTunnel.lock())
{
LogPrint(eLogDebug, "HTTPServer: TUNNEL_OPENJS", tunnel->GetName());
if (tunnel->GetName() == tunnelname)
{
uint16_t rpcPort = server->GetAcceptor().local_endpoint().port();
return { rpcPort, rpcPath };
}
}
}
}
return { 0, "" };
};
auto [rpcPort, rpcPath] = findTunnelEndpoint(tunnelname);
if (rpcPort && rpcPath.length())
{
std::string tjs_file = i2p::fs::DataDirPath("webconsole/torrent-rpc.js");
std::string tclient_file = i2p::fs::DataDirPath("webconsole/torrent_client.html");
if (i2p::fs::Exists(tjs_file))
{
std::ifstream f(tjs_file, std::ifstream::binary);
std::stringstream tjsss;
tjsss << f.rdbuf();
s << "<script>" << tjsss.str() << "</script>\r\n";
s << "<script>window.tjs = new TorrentClient('127.0.0.1', " << rpcPort << ", '" << rpcPath << "');</script>\r\n";
}
else
s << "<h1 style='color:red'>Not found $datadir/webconsole/torrent-rpc.js</h1>\r\n";
if (i2p::fs::Exists(tclient_file))
{
std::ifstream f(tclient_file, std::ifstream::binary);
s << f.rdbuf() << "\r\n";
}
else
s << "<h1 style='color:red'>Not found $datadir/webconsole/torrent_client.html</h1>\r\n";
}
else
s << "<h1 style='color:red'>Not found rpc tunnel " << tunnelname << "</h1>\r\n";
#else
s << "<h1 style='color:red'>Torrents are not supported</h1>\r\n";
#endif
}
HTTPConnection::HTTPConnection (std::string hostname, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
m_Socket (socket), m_BufferLen (0), expected_host(hostname)
{
@@ -1521,8 +1455,6 @@ namespace http {
ShowSAMSession (s, params["sam_id"]);
else if (page == HTTP_PAGE_I2P_TUNNELS)
ShowI2PTunnels (s);
else if (page == OPENJS_TORRENT_CLIENT_PAGE)
ShowTorrentsPage (s, params);
else if (page == HTTP_PAGE_LEASESETS)
ShowLeasesSets(s);
else {
-1
View File
@@ -121,7 +121,6 @@ namespace config {
("http.lang", value<std::string>()->default_value("english"), "WebUI language (default: english )")
("http.showTotalTCSR", value<bool>()->default_value(false), "Show additional value with total TCSR since router's start (default: false)")
("http.theme", value<std::string>()->default_value("light"), "Theme for http web console")
("http.javascript", value<bool>()->default_value(true), "Allow pages with javascript")
;
options_description httpproxy("HTTP Proxy options");