diff --git a/src/Jackett/Content/custom.js b/src/Jackett/Content/custom.js index 4832911ca780a0f46d85827a17f259956ddfc581..f0bb94de78f2ac2bc7db20b6ded37f8216e03dc5 100644 --- a/src/Jackett/Content/custom.js +++ b/src/Jackett/Content/custom.js @@ -1,15 +1,17 @@ -$(document).ready(function () { +var basePath = ""; + +$(document).ready(function () { $.ajaxSetup({ cache: false }); window.jackettIsLocal = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1'; bindUIButtons(); - reloadIndexers(); loadJackettSettings(); + reloadIndexers(); }); function getJackettConfig(callback) { - var jqxhr = $.get("/admin/get_jackett_config", function (data) { + var jqxhr = $.get("get_jackett_config", function (data) { callback(data); }).fail(function () { @@ -22,6 +24,8 @@ function loadJackettSettings() { $("#api-key-input").val(data.config.api_key); $("#app-version").html(data.app_version); $("#jackett-port").val(data.config.port); + $("#jackett-basepathoverride").val(data.config.basepathoverride); + basePath = data.config.basepathoverride; $("#jackett-savedir").val(data.config.blackholedir); $("#jackett-allowext").attr('checked', data.config.external); $("#jackett-allowupdate").attr('checked', data.config.updatedisabled); @@ -39,7 +43,7 @@ function reloadIndexers() { $('#indexers').hide(); $('#indexers > .indexer').remove(); $('#unconfigured-indexers').empty(); - var jqxhr = $.get("/admin/get_indexers", function (data) { + var jqxhr = $.get("get_indexers", function (data) { displayIndexers(data.items); }).fail(function () { doNotify("Error loading indexers, request to Jackett server failed", "danger", "glyphicon glyphicon-alert"); @@ -52,8 +56,8 @@ function displayIndexers(items) { $('#unconfigured-indexers-template').empty(); for (var i = 0; i < items.length; i++) { var item = items[i]; - item.torznab_host = resolveUrl("/torznab/" + item.id); - item.potato_host = resolveUrl("/potato/" + item.id); + item.torznab_host = resolveUrl("/" + basePath + "/torznab/" + item.id); + item.potato_host = resolveUrl("/" + basePath + "/potato/" + item.id); if (item.configured) $('#indexers').append(indexerTemplate(item)); else @@ -92,7 +96,7 @@ function prepareDeleteButtons() { var $btn = $(btn); var id = $btn.data("id"); $btn.click(function () { - var jqxhr = $.post("/admin/delete_indexer", JSON.stringify({ indexer: id }), function (data) { + var jqxhr = $.post("delete_indexer", JSON.stringify({ indexer: id }), function (data) { if (data.result == "error") { doNotify("Delete error for " + id + "\n" + data.error, "danger", "glyphicon glyphicon-alert"); } @@ -125,7 +129,7 @@ function prepareTestButtons() { var id = $btn.data("id"); $btn.click(function () { doNotify("Test started for " + id, "info", "glyphicon glyphicon-transfer"); - var jqxhr = $.post("/admin/test_indexer", JSON.stringify({ indexer: id }), function (data) { + var jqxhr = $.post("test_indexer", JSON.stringify({ indexer: id }), function (data) { if (data.result == "error") { doNotify("Test failed for " + id + ": \n" + data.error, "danger", "glyphicon glyphicon-alert"); } @@ -141,7 +145,7 @@ function prepareTestButtons() { function displayIndexerSetup(id, link) { - var jqxhr = $.post("/admin/get_config_form", JSON.stringify({ indexer: id }), function (data) { + var jqxhr = $.post("get_config_form", JSON.stringify({ indexer: id }), function (data) { if (data.result == "error") { doNotify("Error: " + data.error, "danger", "glyphicon glyphicon-alert"); return; @@ -247,7 +251,7 @@ function populateSetupForm(indexerId, name, config, caps, link) { $goButton.prop('disabled', true); $goButton.html($('#spinner').html()); - var jqxhr = $.post("/admin/configure_indexer", JSON.stringify(data), function (data) { + var jqxhr = $.post("configure_indexer", JSON.stringify(data), function (data) { if (data.result == "error") { if (data.config) { populateConfigItems(configForm, data.config); @@ -321,7 +325,7 @@ function bindUIButtons() { }); $("#jackett-show-releases").click(function () { - var jqxhr = $.get("/admin/GetCache", function (data) { + var jqxhr = $.get("GetCache", function (data) { var releaseTemplate = Handlebars.compile($("#jackett-releases").html()); var item = { releases: data, Title: 'Releases' }; var releaseDialog = $(releaseTemplate(item)); @@ -403,7 +407,7 @@ function bindUIButtons() { $("#jackett-show-search").click(function () { $('#select-indexer-modal').remove(); - var jqxhr = $.get("/admin/get_indexers", function (data) { + var jqxhr = $.get("get_indexers", function (data) { var scope = { items: data.items }; @@ -459,7 +463,7 @@ function bindUIButtons() { $('#searchResults').empty(); $('#jackett-search-perform').html($('#spinner').html()); - var jqxhr = $.post("/admin/search", queryObj, function (data) { + var jqxhr = $.post("search", queryObj, function (data) { $('#jackett-search-perform').html('Search trackers'); var resultsTemplate = Handlebars.compile($("#jackett-search-results").html()); var results = $('#searchResults'); @@ -534,7 +538,7 @@ function bindUIButtons() { }); $("#view-jackett-logs").click(function () { - var jqxhr = $.get("/admin/GetLogs", function (data) { + var jqxhr = $.get("GetLogs", function (data) { var releaseTemplate = Handlebars.compile($("#jackett-logs").html()); var item = { logs: data }; var releaseDialog = $(releaseTemplate(item)); @@ -548,6 +552,7 @@ function bindUIButtons() { $("#change-jackett-port").click(function () { var jackett_port = $("#jackett-port").val(); + var jackett_basepathoverride = $("#jackett-basepathoverride").val(); var jackett_external = $("#jackett-allowext").is(':checked'); var jackett_update = $("#jackett-allowupdate").is(':checked'); var jackett_prerelease = $("#jackett-prerelease").is(':checked'); @@ -558,21 +563,17 @@ function bindUIButtons() { updatedisabled: jackett_update, prerelease: jackett_prerelease, blackholedir: $("#jackett-savedir").val(), - logging: jackett_logging + logging: jackett_logging, + basepathoverride: jackett_basepathoverride }; - var jqxhr = $.post("/admin/set_config", JSON.stringify(jsonObject), function (data) { + var jqxhr = $.post("set_config", JSON.stringify(jsonObject), function (data) { if (data.result == "error") { doNotify("Error: " + data.error, "danger", "glyphicon glyphicon-alert"); return; } else { doNotify("Redirecting you to complete configuration update..", "success", "glyphicon glyphicon-ok"); window.setTimeout(function () { - url = window.location.href; - if (data.external) { - window.location.href = url.substr(0, url.lastIndexOf(":") + 1) + data.port; - } else { - window.location.href = 'http://127.0.0.1:' + data.port; - } + window.location.reload(true); }, 3000); } @@ -582,7 +583,7 @@ function bindUIButtons() { }); $("#trigger-updater").click(function () { - var jqxhr = $.get("/admin/trigger_update", function (data) { + var jqxhr = $.get("trigger_update", function (data) { if (data.result == "error") { doNotify("Error: " + data.error, "danger", "glyphicon glyphicon-alert"); return; @@ -598,7 +599,7 @@ function bindUIButtons() { var password = $("#jackett-adminpwd").val(); var jsonObject = { password: password }; - var jqxhr = $.post("/admin/set_admin_password", JSON.stringify(jsonObject), function (data) { + var jqxhr = $.post("set_admin_password", JSON.stringify(jsonObject), function (data) { if (data.result == "error") { doNotify("Error: " + data.error, "danger", "glyphicon glyphicon-alert"); diff --git a/src/Jackett/Content/index.html b/src/Jackett/Content/index.html index 51591b9c082a318ff4b86eae4fbdfa8a7ec8ae52..94fea8d8febf00b6c04aaf122c16cfb7fe8de0fa 100644 --- a/src/Jackett/Content/index.html +++ b/src/Jackett/Content/index.html @@ -4,27 +4,27 @@ <head> <meta charset="utf-8" /> <link rel='shortcut icon' type='image/x-icon' href='/favicon.ico' /> - <script src="../../libs/filesize.min.js"></script> - <script src="../../libs/jquery.min.js"></script> - <script src="../../libs/jquery.dataTables.min.js"></script> - <script src="../../libs/handlebars.min.js"></script> - <script src="../../libs/moment.min.js"></script> - <script src="../../libs/handlebarsmoment.js"></script> - <script src="../../bootstrap/bootstrap.min.js"></script> - <script src="../../libs/bootstrap-notify.js"></script> + <script src="../libs/filesize.min.js"></script> + <script src="../libs/jquery.min.js"></script> + <script src="../libs/jquery.dataTables.min.js"></script> + <script src="../libs/handlebars.min.js"></script> + <script src="../libs/moment.min.js"></script> + <script src="../libs/handlebarsmoment.js"></script> + <script src="../bootstrap/bootstrap.min.js"></script> + <script src="../libs/bootstrap-notify.js"></script> <script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer></script> - <link href="../../bootstrap/bootstrap.min.css" rel="stylesheet"> - <link href="../../animate.css" rel="stylesheet"> - <link href="../../custom.css" rel="stylesheet"> - <link href="../../css/jquery.dataTables.css" rel="stylesheet"> - <link rel="stylesheet" href="../../css/font-awesome.min.css"> + <link href="../bootstrap/bootstrap.min.css" rel="stylesheet"> + <link href="../animate.css" rel="stylesheet"> + <link href="../custom.css" rel="stylesheet"> + <link href="../css/jquery.dataTables.css" rel="stylesheet"> + <link rel="stylesheet" href="../css/font-awesome.min.css"> <title>Jackett</title> </head> <body> <div id="page"> - <img id="logo" src="../../jacket_medium.png" alt="Logo" /><span id="header-title">Jackett</span> + <img id="logo" src="../jacket_medium.png" alt="Logo" /><span id="header-title">Jackett</span> <div class="pull-right jackett-apikey"> <span class="input-header">API Key: </span> @@ -84,10 +84,13 @@ Logout </a> </div> + <div class="input-area"> + <span class="input-header">Base Path Override: </span> + <input id="jackett-basepathoverride" class="form-control input-right" type="text" value="" placeholder="jackett/"> + </div> <div class="input-area"> <span class="input-header">Server port: </span> <input id="jackett-port" class="form-control input-right" type="text" value="" placeholder="9117"> - </div> <div class="input-area"> <span class="input-header">Manual download blackhole directory: </span> @@ -172,7 +175,7 @@ <div class="indexer-logo"> <!-- Make section browser searchable --> <span class="hidden-name">{{name}}</span> - <img alt="{{name}}" title="{{name}}" src="../../logos/{{id}}.png" /> + <img alt="{{name}}" title="{{name}}" src="../logos/{{id}}.png" /> </div> <div class="indexer-buttons"> <button class="btn btn-primary btn-sm indexer-setup" data-id="{{id}}" data-link="{{site_link}}"> @@ -206,7 +209,7 @@ <div class="indexer-logo indexer-setup" data-id="{{id}}" data-link="{{site_link}}"> <!-- Make section browser searchable --> <span class="hidden-name">{{name}}</span> - <img alt="{{name}}" title="{{name}}" src="../../logos/{{id}}.png" /> + <img alt="{{name}}" title="{{name}}" src="../logos/{{id}}.png" /> </div> </div> @@ -478,6 +481,6 @@ <span class="spinner glyphicon glyphicon-refresh"></span> </script> - <script src="../../custom.js"></script> + <script src="../custom.js"></script> </body> </html> diff --git a/src/Jackett/Content/login.html b/src/Jackett/Content/login.html index 194ad4bcdb694114f7e34b6cb3a33fe76b35a596..9f847549424e8e1602dd8723edf3d1a7d11d2705 100644 --- a/src/Jackett/Content/login.html +++ b/src/Jackett/Content/login.html @@ -6,28 +6,28 @@ <link rel='shortcut icon' type='image/x-icon' href='/favicon.ico' /> - <script src="../../libs/jquery.min.js"></script> - <script src="../../libs/jquery.dataTables.min.js"></script> - <script src="../../libs/handlebars.min.js"></script> - <script src="../../libs/moment.min.js"></script> - <script src="../../libs/handlebarsmoment.js"></script> - <script src="../../bootstrap/bootstrap.min.js"></script> - <script src="../../libs/bootstrap-notify.js"></script> + <script src="../libs/jquery.min.js"></script> + <script src="../libs/jquery.dataTables.min.js"></script> + <script src="../libs/handlebars.min.js"></script> + <script src="../libs/moment.min.js"></script> + <script src="../libs/handlebarsmoment.js"></script> + <script src="../bootstrap/bootstrap.min.js"></script> + <script src="../libs/bootstrap-notify.js"></script> - <link href="../../bootstrap/bootstrap.min.css" rel="stylesheet"> - <link href="../../animate.css" rel="stylesheet"> - <link href="../../custom.css" rel="stylesheet"> + <link href="../bootstrap/bootstrap.min.css" rel="stylesheet"> + <link href="../animate.css" rel="stylesheet"> + <link href="../custom.css" rel="stylesheet"> <title>Jackett</title> </head> <body> <div id="page"> - <img id="logo" src="../../jacket_medium.png" /><span id="header-title">Jackett</span> + <img id="logo" src="../jacket_medium.png" /><span id="header-title">Jackett</span> <hr /> <h1>Login</h1> - <form action="../Admin/Dashboard" method="post"> + <form action="Dashboard" method="post"> <div class="input-area"> <span class="input-header">Admin password</span> <input id="password" name="password" class="form-control input-right" type="password"> diff --git a/src/Jackett/Controllers/AdminController.cs b/src/Jackett/Controllers/AdminController.cs index 6750267dd6fafea65c9843bf394f685efe4d4ab7..5682938ca4fb54ba1810f48bc3b3636e029369dc 100644 --- a/src/Jackett/Controllers/AdminController.cs +++ b/src/Jackett/Controllers/AdminController.cs @@ -80,7 +80,7 @@ namespace Jackett.Controllers var ctx = Request.GetOwinContext(); var authManager = ctx.Authentication; authManager.SignOut("ApplicationCookie"); - return Redirect("/Admin/Dashboard"); + return Redirect("Admin/Dashboard"); } [HttpGet] @@ -318,6 +318,7 @@ namespace Jackett.Controllers cfg["prerelease"] = serverService.Config.UpdatePrerelease; cfg["password"] = string.IsNullOrEmpty(serverService.Config.AdminPassword) ? string.Empty : serverService.Config.AdminPassword.Substring(0, 10); cfg["logging"] = Startup.TracingEnabled; + cfg["basepathoverride"] = serverService.Config.BasePathOverride; jsonReply["config"] = cfg; @@ -349,9 +350,12 @@ namespace Jackett.Controllers bool updateDisabled = (bool)postData["updatedisabled"]; bool preRelease = (bool)postData["prerelease"]; bool logging = (bool)postData["logging"]; + string basePathOverride = (string)postData["basepathoverride"]; Engine.Server.Config.UpdateDisabled = updateDisabled; Engine.Server.Config.UpdatePrerelease = preRelease; + Engine.Server.Config.BasePathOverride = basePathOverride; + Startup.BasePath = Engine.Server.BasePath(); Engine.Server.SaveConfig(); Engine.SetLogLevel(logging ? LogLevel.Debug : LogLevel.Info); @@ -446,7 +450,7 @@ namespace Jackett.Controllers private void ConfigureCacheResults(List<TrackerCacheResult> results) { - var serverUrl = string.Format("{0}://{1}:{2}/", Request.RequestUri.Scheme, Request.RequestUri.Host, Request.RequestUri.Port); + var serverUrl = string.Format("{0}://{1}:{2}{3}", Request.RequestUri.Scheme, Request.RequestUri.Host, Request.RequestUri.Port, serverService.BasePath()); foreach (var result in results) { var link = result.Link; diff --git a/src/Jackett/Controllers/PotatoController.cs b/src/Jackett/Controllers/PotatoController.cs index 86992829c7de2b29feedb66d10a790e29165f337..dff0f7021b34d448f0fccd8ea3be1d7273e8242f 100644 --- a/src/Jackett/Controllers/PotatoController.cs +++ b/src/Jackett/Controllers/PotatoController.cs @@ -117,7 +117,7 @@ namespace Jackett.Controllers } releases = indexer.FilterResults(torznabQuery, releases); - var serverUrl = string.Format("{0}://{1}:{2}/", Request.RequestUri.Scheme, Request.RequestUri.Host, Request.RequestUri.Port); + var serverUrl = string.Format("{0}://{1}:{2}{3}", Request.RequestUri.Scheme, Request.RequestUri.Host, Request.RequestUri.Port, serverService.BasePath()); var potatoResponse = new TorrentPotatoResponse(); releases = TorznabUtil.FilterResultsToTitle(releases, torznabQuery.SanitizedSearchTerm, year); diff --git a/src/Jackett/Controllers/TorznabController.cs b/src/Jackett/Controllers/TorznabController.cs index 225066d1630e1ebb9dd5a2de1b9cc9bb19fa1d77..224f006687f3e4b6b2ad533dca6d7864651e8e80 100644 --- a/src/Jackett/Controllers/TorznabController.cs +++ b/src/Jackett/Controllers/TorznabController.cs @@ -100,7 +100,7 @@ namespace Jackett.Controllers logger.Info(logBuilder.ToString()); - var serverUrl = string.Format("{0}://{1}:{2}/", Request.RequestUri.Scheme, Request.RequestUri.Host, Request.RequestUri.Port); + var serverUrl = string.Format("{0}://{1}:{2}{3}", Request.RequestUri.Scheme, Request.RequestUri.Host, Request.RequestUri.Port, serverService.BasePath()); var resultPage = new ResultPage(new ChannelInfo { Title = indexer.DisplayName, diff --git a/src/Jackett/Models/Config/ServerConfig.cs b/src/Jackett/Models/Config/ServerConfig.cs index eda58eab76323370da0f24f2e783d7cd8d4e9572..5b18ee2cd29d8bb8b3eb976e10a53134360ebd26 100644 --- a/src/Jackett/Models/Config/ServerConfig.cs +++ b/src/Jackett/Models/Config/ServerConfig.cs @@ -23,6 +23,7 @@ namespace Jackett.Models.Config public string BlackholeDir { get; set; } public bool UpdateDisabled { get; set; } public bool UpdatePrerelease { get; set; } + public string BasePathOverride { get; set; } public string[] GetListenAddresses(bool? external = null) { diff --git a/src/Jackett/Services/ServerService.cs b/src/Jackett/Services/ServerService.cs index bcfda2a704eea1c4135faf889e8e82dcad8758d7..e7fcba27b073aa382f2ed8ee834af10e7db11c5e 100644 --- a/src/Jackett/Services/ServerService.cs +++ b/src/Jackett/Services/ServerService.cs @@ -19,6 +19,7 @@ using System.Net; using System.Net.NetworkInformation; using System.Security.Cryptography; using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Web; @@ -32,7 +33,8 @@ namespace Jackett.Services void ReserveUrls(bool doInstall = true); ServerConfig Config { get; } void SaveConfig(); - Uri ConvertToProxyLink(Uri link, string serverUrl, string indexerId, string action = "dl", string file = "t.torrent"); + Uri ConvertToProxyLink(Uri link, string serverUrl, string indexerId, string action = "dl", string file = "t.torrent"); + string BasePath(); } public class ServerService : IServerService @@ -77,6 +79,23 @@ namespace Jackett.Services return new Uri(proxyLink); } + public string BasePath() + { + if (config.BasePathOverride == null || config.BasePathOverride == "") { + return "/"; + } + var path = config.BasePathOverride; + if (!path.EndsWith("/")) + { + path = path + "/"; + } + if (!path.StartsWith("/")) + { + path = "/" + path; + } + return path; + } + private void LoadConfig() { // Load config @@ -139,6 +158,7 @@ namespace Jackett.Services logger.Info("Starting web server at " + config.GetListenAddresses()[0]); var startOptions = new StartOptions(); config.GetListenAddresses().ToList().ForEach(u => startOptions.Urls.Add(u)); + Startup.BasePath = BasePath(); _server = WebApp.Start<Startup>(startOptions); logger.Debug("Web server started"); updater.StartUpdateChecker(); diff --git a/src/Jackett/Startup.cs b/src/Jackett/Startup.cs index 5aa8df8edba5b8e35a9443977fddc1f048e29784..3fb1814ba11845b1384496e1f75366bd3da60d48 100644 --- a/src/Jackett/Startup.cs +++ b/src/Jackett/Startup.cs @@ -58,6 +58,12 @@ namespace Jackett set; } + public static string BasePath + { + get; + set; + } + public void Configuration(IAppBuilder appBuilder) { // Configure Web API for self-host. diff --git a/src/Jackett/Utils/WebApiRootRedirectMiddleware.cs b/src/Jackett/Utils/WebApiRootRedirectMiddleware.cs index 68959f76d24e879b8f07401ed24858ad890e077d..fe4f7ac567c745f62817fb463678993d64516d99 100644 --- a/src/Jackett/Utils/WebApiRootRedirectMiddleware.cs +++ b/src/Jackett/Utils/WebApiRootRedirectMiddleware.cs @@ -1,4 +1,5 @@ -using Microsoft.Owin; +using Jackett.Services; +using Microsoft.Owin; using System; using System.Collections.Generic; using System.Linq; @@ -11,7 +12,7 @@ namespace Jackett.Utils { public WebApiRootRedirectMiddleware(OwinMiddleware next) : base(next) - { + { } public async override Task Invoke(IOwinContext context) @@ -21,7 +22,9 @@ namespace Jackett.Utils { // 301 is the status code of permanent redirect context.Response.StatusCode = 301; - context.Response.Headers.Set("Location", "/Admin/Dashboard"); + var redir = Startup.BasePath + "Admin/Dashboard"; + Engine.Logger.Info("redirecting to " + redir); + context.Response.Headers.Set("Location", redir); } else {