mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2026-10-22 21:51:51 +00:00
remove ShowTorrentPage from webconsole
This commit is contained in:
1 parent
a2cf28cb04
commit
9cd8036b3b
4 files changed
-486
No files matched your search
@@ -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();
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in new issue
Block a user