2023-08-24 13:35:07 +02:00
|
|
|
|
using System.Net;
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
using System.Text;
|
2023-08-26 01:47:36 +02:00
|
|
|
|
using System.Text.RegularExpressions;
|
2023-08-24 13:35:07 +02:00
|
|
|
|
using Newtonsoft.Json;
|
2023-08-26 02:43:24 +02:00
|
|
|
|
using Tranga.Jobs;
|
2023-08-27 01:01:39 +02:00
|
|
|
|
using Tranga.LibraryConnectors;
|
2023-08-26 01:47:36 +02:00
|
|
|
|
using Tranga.MangaConnectors;
|
2023-08-27 01:01:39 +02:00
|
|
|
|
using Tranga.NotificationConnectors;
|
2023-08-24 13:35:07 +02:00
|
|
|
|
|
|
|
|
|
namespace Tranga;
|
|
|
|
|
|
|
|
|
|
public class Server : GlobalBase
|
|
|
|
|
{
|
|
|
|
|
private readonly HttpListener _listener = new ();
|
|
|
|
|
private readonly Tranga _parent;
|
|
|
|
|
|
|
|
|
|
public Server(Tranga parent) : base(parent)
|
|
|
|
|
{
|
|
|
|
|
this._parent = parent;
|
|
|
|
|
if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
|
|
|
|
this._listener.Prefixes.Add($"http://*:{settings.apiPortNumber}/");
|
|
|
|
|
else
|
|
|
|
|
this._listener.Prefixes.Add($"http://localhost:{settings.apiPortNumber}/");
|
2023-08-29 12:40:10 +02:00
|
|
|
|
Thread listenThread = new (Listen);
|
|
|
|
|
listenThread.Start();
|
|
|
|
|
Thread watchThread = new(WatchRunning);
|
|
|
|
|
watchThread.Start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void WatchRunning()
|
|
|
|
|
{
|
|
|
|
|
while(_parent.keepRunning)
|
|
|
|
|
Thread.Sleep(1000);
|
|
|
|
|
this._listener.Close();
|
2023-08-24 13:35:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Listen()
|
|
|
|
|
{
|
|
|
|
|
this._listener.Start();
|
|
|
|
|
foreach(string prefix in this._listener.Prefixes)
|
|
|
|
|
Log($"Listening on {prefix}");
|
|
|
|
|
while (this._listener.IsListening && _parent.keepRunning)
|
|
|
|
|
{
|
2023-08-29 12:40:10 +02:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
HttpListenerContext context = this._listener.GetContext();
|
2023-09-02 14:11:11 +02:00
|
|
|
|
//Log($"{context.Request.HttpMethod} {context.Request.Url} {context.Request.UserAgent}");
|
2023-08-29 12:40:10 +02:00
|
|
|
|
Task t = new(() =>
|
|
|
|
|
{
|
|
|
|
|
HandleRequest(context);
|
|
|
|
|
});
|
|
|
|
|
t.Start();
|
|
|
|
|
}
|
|
|
|
|
catch (HttpListenerException e)
|
2023-08-24 13:35:07 +02:00
|
|
|
|
{
|
2023-08-29 12:40:10 +02:00
|
|
|
|
|
|
|
|
|
}
|
2023-08-24 13:35:07 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void HandleRequest(HttpListenerContext context)
|
|
|
|
|
{
|
|
|
|
|
HttpListenerRequest request = context.Request;
|
|
|
|
|
HttpListenerResponse response = context.Response;
|
2023-08-26 01:47:36 +02:00
|
|
|
|
if(request.HttpMethod == "OPTIONS")
|
|
|
|
|
SendResponse(HttpStatusCode.OK, context.Response);
|
2023-08-24 13:35:07 +02:00
|
|
|
|
if(request.Url!.LocalPath.Contains("favicon"))
|
|
|
|
|
SendResponse(HttpStatusCode.NoContent, response);
|
2023-08-26 01:47:36 +02:00
|
|
|
|
|
|
|
|
|
switch (request.HttpMethod)
|
|
|
|
|
{
|
|
|
|
|
case "GET":
|
2023-08-27 01:05:32 +02:00
|
|
|
|
HandleGet(request, response);
|
2023-08-26 01:47:36 +02:00
|
|
|
|
break;
|
|
|
|
|
case "POST":
|
2023-08-27 01:05:32 +02:00
|
|
|
|
HandlePost(request, response);
|
2023-08-26 01:47:36 +02:00
|
|
|
|
break;
|
|
|
|
|
case "DELETE":
|
2023-08-27 01:05:32 +02:00
|
|
|
|
HandleDelete(request, response);
|
2023-08-26 01:47:36 +02:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Dictionary<string, string> GetRequestVariables(string query)
|
|
|
|
|
{
|
|
|
|
|
Dictionary<string, string> ret = new();
|
|
|
|
|
Regex queryRex = new (@"\?{1}&?([A-z0-9-=]+=[A-z0-9-=]+)+(&[A-z0-9-=]+=[A-z0-9-=]+)*");
|
|
|
|
|
if (!queryRex.IsMatch(query))
|
|
|
|
|
return ret;
|
|
|
|
|
query = query.Substring(1);
|
2023-08-27 01:05:32 +02:00
|
|
|
|
foreach (string keyValuePair in query.Split('&').Where(str => str.Length >= 3))
|
2023-08-26 01:47:36 +02:00
|
|
|
|
{
|
2023-08-27 01:05:32 +02:00
|
|
|
|
string var = keyValuePair.Split('=')[0];
|
|
|
|
|
string val = Regex.Replace(keyValuePair.Substring(var.Length + 1), "%20", " ");
|
2023-08-26 01:47:36 +02:00
|
|
|
|
val = Regex.Replace(val, "%[0-9]{2}", "");
|
|
|
|
|
ret.Add(var, val);
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-27 01:05:32 +02:00
|
|
|
|
private void HandleGet(HttpListenerRequest request, HttpListenerResponse response)
|
2023-08-26 01:47:36 +02:00
|
|
|
|
{
|
|
|
|
|
Dictionary<string, string> requestVariables = GetRequestVariables(request.Url!.Query);
|
2023-09-01 21:40:56 +02:00
|
|
|
|
string? connectorName, jobId, internalId;
|
|
|
|
|
MangaConnector? connector;
|
|
|
|
|
Manga? manga;
|
2023-08-26 02:43:24 +02:00
|
|
|
|
string path = Regex.Match(request.Url!.LocalPath, @"[A-z0-9]+(\/[A-z0-9]+)*").Value;
|
|
|
|
|
switch (path)
|
2023-08-26 01:47:36 +02:00
|
|
|
|
{
|
|
|
|
|
case "Connectors":
|
2023-08-27 01:05:32 +02:00
|
|
|
|
SendResponse(HttpStatusCode.OK, response, _parent.GetConnectors().Select(con => con.name).ToArray());
|
2023-08-26 01:47:36 +02:00
|
|
|
|
break;
|
2023-09-01 21:40:56 +02:00
|
|
|
|
case "Manga/Cover":
|
|
|
|
|
if (!requestVariables.TryGetValue("internalId", out internalId) ||
|
|
|
|
|
!_parent.TryGetPublicationById(internalId, out manga))
|
|
|
|
|
{
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string filePath = settings.GetFullCoverPath((Manga)manga!);
|
|
|
|
|
if (File.Exists(filePath))
|
|
|
|
|
{
|
|
|
|
|
FileStream coverStream = new(filePath, FileMode.Open);
|
|
|
|
|
SendResponse(HttpStatusCode.OK, response, coverStream);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SendResponse(HttpStatusCode.NotFound, response);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2023-08-31 12:16:02 +02:00
|
|
|
|
case "Manga/FromConnector":
|
2023-09-02 22:12:49 +02:00
|
|
|
|
requestVariables.TryGetValue("title", out string? title);
|
|
|
|
|
requestVariables.TryGetValue("url", out string? url);
|
2023-08-26 02:43:24 +02:00
|
|
|
|
if (!requestVariables.TryGetValue("connector", out connectorName) ||
|
2023-09-02 22:12:49 +02:00
|
|
|
|
!_parent.TryGetConnector(connectorName, out connector) ||
|
|
|
|
|
(title is null && url is null))
|
2023-08-26 01:47:36 +02:00
|
|
|
|
{
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
2023-08-26 02:43:24 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
2023-09-02 22:12:49 +02:00
|
|
|
|
|
|
|
|
|
if (url is not null)
|
|
|
|
|
{
|
|
|
|
|
HashSet<Manga> ret = new();
|
|
|
|
|
manga = connector!.GetMangaFromUrl(url);
|
|
|
|
|
if (manga is not null)
|
|
|
|
|
ret.Add((Manga)manga);
|
|
|
|
|
SendResponse(HttpStatusCode.OK, response, ret);
|
|
|
|
|
}else
|
|
|
|
|
SendResponse(HttpStatusCode.OK, response, connector!.GetManga(title!));
|
2023-08-26 02:43:24 +02:00
|
|
|
|
break;
|
2023-08-31 12:16:02 +02:00
|
|
|
|
case "Manga/Chapters":
|
2023-08-26 02:43:24 +02:00
|
|
|
|
if(!requestVariables.TryGetValue("connector", out connectorName) ||
|
2023-09-01 21:40:56 +02:00
|
|
|
|
!requestVariables.TryGetValue("internalId", out internalId) ||
|
|
|
|
|
!_parent.TryGetConnector(connectorName, out connector) ||
|
|
|
|
|
!_parent.TryGetPublicationById(internalId, out manga))
|
2023-08-26 02:43:24 +02:00
|
|
|
|
{
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-09-01 21:40:56 +02:00
|
|
|
|
SendResponse(HttpStatusCode.OK, response, connector!.GetChapters((Manga)manga!));
|
2023-08-26 02:43:24 +02:00
|
|
|
|
break;
|
2023-08-31 12:16:02 +02:00
|
|
|
|
case "Jobs":
|
2023-08-26 02:43:24 +02:00
|
|
|
|
if (!requestVariables.TryGetValue("jobId", out jobId))
|
|
|
|
|
{
|
2023-09-01 23:37:50 +02:00
|
|
|
|
if(!_parent.jobBoss.jobs.Any(jjob => jjob.id == jobId))
|
2023-08-26 02:43:24 +02:00
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
else
|
2023-09-01 23:37:50 +02:00
|
|
|
|
SendResponse(HttpStatusCode.OK, response, _parent.jobBoss.jobs.First(jjob => jjob.id == jobId));
|
2023-08-26 02:43:24 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
2023-09-01 23:37:50 +02:00
|
|
|
|
SendResponse(HttpStatusCode.OK, response, _parent.jobBoss.jobs);
|
2023-08-26 02:43:24 +02:00
|
|
|
|
break;
|
2023-08-31 12:16:02 +02:00
|
|
|
|
case "Jobs/Progress":
|
2023-08-26 02:43:24 +02:00
|
|
|
|
if (!requestVariables.TryGetValue("jobId", out jobId))
|
|
|
|
|
{
|
2023-09-01 23:37:50 +02:00
|
|
|
|
if(!_parent.jobBoss.jobs.Any(jjob => jjob.id == jobId))
|
2023-08-26 02:43:24 +02:00
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
else
|
2023-09-01 23:37:50 +02:00
|
|
|
|
SendResponse(HttpStatusCode.OK, response, _parent.jobBoss.jobs.First(jjob => jjob.id == jobId).progressToken);
|
2023-08-26 02:43:24 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
2023-09-01 23:37:50 +02:00
|
|
|
|
SendResponse(HttpStatusCode.OK, response, _parent.jobBoss.jobs.Select(jjob => jjob.progressToken));
|
2023-08-26 02:43:24 +02:00
|
|
|
|
break;
|
2023-08-31 12:16:02 +02:00
|
|
|
|
case "Jobs/Running":
|
2023-09-01 23:37:50 +02:00
|
|
|
|
SendResponse(HttpStatusCode.OK, response, _parent.jobBoss.jobs.Where(jjob => jjob.progressToken.state is ProgressToken.State.Running));
|
2023-08-26 02:43:24 +02:00
|
|
|
|
break;
|
2023-08-31 12:16:02 +02:00
|
|
|
|
case "Jobs/Waiting":
|
2023-09-01 23:37:50 +02:00
|
|
|
|
SendResponse(HttpStatusCode.OK, response, _parent.jobBoss.jobs.Where(jjob => jjob.progressToken.state is ProgressToken.State.Standby));
|
2023-08-26 02:43:24 +02:00
|
|
|
|
break;
|
2023-08-31 16:40:08 +02:00
|
|
|
|
case "Jobs/MonitorJobs":
|
2023-09-01 23:37:50 +02:00
|
|
|
|
SendResponse(HttpStatusCode.OK, response, _parent.jobBoss.jobs.Where(jjob => jjob is DownloadNewChapters));
|
2023-08-31 16:40:08 +02:00
|
|
|
|
break;
|
2023-08-26 02:43:24 +02:00
|
|
|
|
case "Settings":
|
|
|
|
|
SendResponse(HttpStatusCode.OK, response, settings);
|
|
|
|
|
break;
|
|
|
|
|
case "NotificationConnectors":
|
|
|
|
|
SendResponse(HttpStatusCode.OK, response, notificationConnectors);
|
|
|
|
|
break;
|
2023-08-31 15:41:29 +02:00
|
|
|
|
case "NotificationConnectors/Types":
|
|
|
|
|
SendResponse(HttpStatusCode.OK, response,
|
2023-08-31 16:02:02 +02:00
|
|
|
|
Enum.GetValues<NotificationConnector.NotificationConnectorType>().Select(nc => new KeyValuePair<byte, string?>((byte)nc, Enum.GetName(nc))));
|
2023-08-27 01:01:39 +02:00
|
|
|
|
break;
|
2023-08-26 02:43:24 +02:00
|
|
|
|
case "LibraryConnectors":
|
|
|
|
|
SendResponse(HttpStatusCode.OK, response, libraryConnectors);
|
|
|
|
|
break;
|
2023-08-27 01:01:39 +02:00
|
|
|
|
case "LibraryConnectors/Types":
|
2023-08-31 15:41:29 +02:00
|
|
|
|
SendResponse(HttpStatusCode.OK, response,
|
|
|
|
|
Enum.GetValues<LibraryConnector.LibraryType>().Select(lc => new KeyValuePair<byte, string?>((byte)lc, Enum.GetName(lc))));
|
2023-08-27 01:01:39 +02:00
|
|
|
|
break;
|
2023-08-26 02:43:24 +02:00
|
|
|
|
default:
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
2023-08-26 01:47:36 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-27 01:05:32 +02:00
|
|
|
|
private void HandlePost(HttpListenerRequest request, HttpListenerResponse response)
|
2023-08-26 01:47:36 +02:00
|
|
|
|
{
|
2023-08-27 01:01:39 +02:00
|
|
|
|
Dictionary<string, string> requestVariables = GetRequestVariables(request.Url!.Query);
|
2023-09-02 14:11:44 +02:00
|
|
|
|
string? connectorName, internalId, jobId;
|
2023-08-27 01:01:39 +02:00
|
|
|
|
MangaConnector connector;
|
2023-08-31 12:16:02 +02:00
|
|
|
|
Manga manga;
|
2023-09-02 14:11:44 +02:00
|
|
|
|
Job? job;
|
2023-08-27 01:01:39 +02:00
|
|
|
|
string path = Regex.Match(request.Url!.LocalPath, @"[A-z0-9]+(\/[A-z0-9]+)*").Value;
|
|
|
|
|
switch (path)
|
|
|
|
|
{
|
2023-08-31 12:16:02 +02:00
|
|
|
|
case "Jobs/MonitorManga":
|
2023-08-27 01:01:39 +02:00
|
|
|
|
if(!requestVariables.TryGetValue("connector", out connectorName) ||
|
|
|
|
|
!requestVariables.TryGetValue("internalId", out internalId) ||
|
|
|
|
|
!requestVariables.TryGetValue("interval", out string? intervalStr) ||
|
|
|
|
|
_parent.GetConnector(connectorName) is null ||
|
|
|
|
|
_parent.GetPublicationById(internalId) is null ||
|
|
|
|
|
!TimeSpan.TryParse(intervalStr, out TimeSpan interval))
|
|
|
|
|
{
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
connector = _parent.GetConnector(connectorName)!;
|
2023-08-31 12:16:02 +02:00
|
|
|
|
manga = (Manga)_parent.GetPublicationById(internalId)!;
|
2023-09-01 23:37:50 +02:00
|
|
|
|
_parent.jobBoss.AddJob(new DownloadNewChapters(this, connector, manga, true, interval));
|
2023-08-27 01:01:39 +02:00
|
|
|
|
SendResponse(HttpStatusCode.Accepted, response);
|
|
|
|
|
break;
|
2023-08-31 12:16:02 +02:00
|
|
|
|
case "Jobs/DownloadNewChapters":
|
2023-08-27 01:01:39 +02:00
|
|
|
|
if(!requestVariables.TryGetValue("connector", out connectorName) ||
|
|
|
|
|
!requestVariables.TryGetValue("internalId", out internalId) ||
|
|
|
|
|
_parent.GetConnector(connectorName) is null ||
|
|
|
|
|
_parent.GetPublicationById(internalId) is null)
|
|
|
|
|
{
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
connector = _parent.GetConnector(connectorName)!;
|
2023-08-31 12:16:02 +02:00
|
|
|
|
manga = (Manga)_parent.GetPublicationById(internalId)!;
|
2023-09-01 23:37:50 +02:00
|
|
|
|
_parent.jobBoss.AddJob(new DownloadNewChapters(this, connector, manga, false));
|
2023-08-27 01:01:39 +02:00
|
|
|
|
SendResponse(HttpStatusCode.Accepted, response);
|
|
|
|
|
break;
|
2023-08-31 13:12:03 +02:00
|
|
|
|
case "Jobs/StartNow":
|
2023-09-02 14:11:44 +02:00
|
|
|
|
if (!requestVariables.TryGetValue("jobId", out jobId) ||
|
|
|
|
|
!_parent.jobBoss.TryGetJobById(jobId, out job))
|
2023-08-31 13:12:03 +02:00
|
|
|
|
{
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-09-01 23:37:50 +02:00
|
|
|
|
_parent.jobBoss.AddJobToQueue(job!);
|
2023-08-31 13:12:03 +02:00
|
|
|
|
SendResponse(HttpStatusCode.Accepted, response);
|
|
|
|
|
break;
|
2023-09-02 14:13:15 +02:00
|
|
|
|
case "Jobs/Cancel":
|
|
|
|
|
if (!requestVariables.TryGetValue("jobId", out jobId) ||
|
|
|
|
|
!_parent.jobBoss.TryGetJobById(jobId, out job))
|
|
|
|
|
{
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
job!.Cancel();
|
|
|
|
|
SendResponse(HttpStatusCode.Accepted, response);
|
|
|
|
|
break;
|
2023-08-27 01:01:39 +02:00
|
|
|
|
case "Settings/UpdateDownloadLocation":
|
|
|
|
|
if (!requestVariables.TryGetValue("downloadLocation", out string? downloadLocation) ||
|
|
|
|
|
!requestVariables.TryGetValue("moveFiles", out string? moveFilesStr) ||
|
|
|
|
|
!Boolean.TryParse(moveFilesStr, out bool moveFiles))
|
|
|
|
|
{
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
settings.UpdateDownloadLocation(downloadLocation, moveFiles);
|
|
|
|
|
SendResponse(HttpStatusCode.Accepted, response);
|
|
|
|
|
break;
|
|
|
|
|
/*case "Settings/UpdateWorkingDirectory":
|
|
|
|
|
if (!requestVariables.TryGetValue("workingDirectory", out string? workingDirectory))
|
|
|
|
|
{
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
settings.UpdateWorkingDirectory(workingDirectory);
|
|
|
|
|
SendResponse(HttpStatusCode.Accepted, response);
|
|
|
|
|
break;*/
|
|
|
|
|
case "NotificationConnectors/Update":
|
|
|
|
|
if (!requestVariables.TryGetValue("notificationConnector", out string? notificationConnectorStr) ||
|
2023-08-31 16:02:02 +02:00
|
|
|
|
!Enum.TryParse(notificationConnectorStr, out NotificationConnector.NotificationConnectorType notificationConnectorType))
|
2023-08-27 01:01:39 +02:00
|
|
|
|
{
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-31 16:02:02 +02:00
|
|
|
|
if (notificationConnectorType is NotificationConnector.NotificationConnectorType.Gotify)
|
2023-08-27 01:01:39 +02:00
|
|
|
|
{
|
|
|
|
|
if (!requestVariables.TryGetValue("gotifyUrl", out string? gotifyUrl) ||
|
|
|
|
|
!requestVariables.TryGetValue("gotifyAppToken", out string? gotifyAppToken))
|
|
|
|
|
{
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
AddNotificationConnector(new Gotify(this, gotifyUrl, gotifyAppToken));
|
|
|
|
|
SendResponse(HttpStatusCode.Accepted, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-31 16:02:02 +02:00
|
|
|
|
if (notificationConnectorType is NotificationConnector.NotificationConnectorType.LunaSea)
|
2023-08-27 01:01:39 +02:00
|
|
|
|
{
|
|
|
|
|
if (!requestVariables.TryGetValue("lunaseaWebhook", out string? lunaseaWebhook))
|
|
|
|
|
{
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
AddNotificationConnector(new LunaSea(this, lunaseaWebhook));
|
|
|
|
|
SendResponse(HttpStatusCode.Accepted, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2023-08-31 16:02:02 +02:00
|
|
|
|
case "LibraryConnectors/Update":
|
|
|
|
|
if (!requestVariables.TryGetValue("libraryConnector", out string? libraryConnectorStr) ||
|
|
|
|
|
!Enum.TryParse(libraryConnectorStr,
|
|
|
|
|
out LibraryConnector.LibraryType libraryConnectorType))
|
2023-08-27 01:01:39 +02:00
|
|
|
|
{
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-31 16:02:02 +02:00
|
|
|
|
if (libraryConnectorType is LibraryConnector.LibraryType.Kavita)
|
2023-08-27 01:01:39 +02:00
|
|
|
|
{
|
|
|
|
|
if (!requestVariables.TryGetValue("kavitaUrl", out string? kavitaUrl) ||
|
|
|
|
|
!requestVariables.TryGetValue("kavitaUsername", out string? kavitaUsername) ||
|
|
|
|
|
!requestVariables.TryGetValue("kavitaPassword", out string? kavitaPassword))
|
|
|
|
|
{
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
AddLibraryConnector(new Kavita(this, kavitaUrl, kavitaUsername, kavitaPassword));
|
|
|
|
|
SendResponse(HttpStatusCode.Accepted, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-31 16:02:02 +02:00
|
|
|
|
if (libraryConnectorType is LibraryConnector.LibraryType.Komga)
|
2023-08-27 01:01:39 +02:00
|
|
|
|
{
|
|
|
|
|
if (!requestVariables.TryGetValue("komgaUrl", out string? komgaUrl) ||
|
|
|
|
|
!requestVariables.TryGetValue("komgaAuth", out string? komgaAuth))
|
|
|
|
|
{
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
AddLibraryConnector(new Komga(this, komgaUrl, komgaAuth));
|
|
|
|
|
SendResponse(HttpStatusCode.Accepted, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-08-26 01:47:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-08-27 01:05:32 +02:00
|
|
|
|
private void HandleDelete(HttpListenerRequest request, HttpListenerResponse response)
|
2023-08-26 01:47:36 +02:00
|
|
|
|
{
|
2023-08-27 01:01:39 +02:00
|
|
|
|
Dictionary<string, string> requestVariables = GetRequestVariables(request.Url!.Query);
|
|
|
|
|
string? connectorName, internalId;
|
|
|
|
|
MangaConnector connector;
|
2023-08-31 12:16:02 +02:00
|
|
|
|
Manga manga;
|
2023-08-27 01:01:39 +02:00
|
|
|
|
string path = Regex.Match(request.Url!.LocalPath, @"[A-z0-9]+(\/[A-z0-9]+)*").Value;
|
|
|
|
|
switch (path)
|
|
|
|
|
{
|
2023-08-31 13:12:03 +02:00
|
|
|
|
case "Jobs":
|
2023-09-02 14:11:44 +02:00
|
|
|
|
if (!requestVariables.TryGetValue("jobId", out string? jobId) ||
|
2023-09-01 23:37:50 +02:00
|
|
|
|
!_parent.jobBoss.TryGetJobById(jobId, out Job? job))
|
2023-08-31 13:12:03 +02:00
|
|
|
|
{
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-09-01 23:37:50 +02:00
|
|
|
|
_parent.jobBoss.RemoveJob(job!);
|
2023-08-31 13:12:03 +02:00
|
|
|
|
SendResponse(HttpStatusCode.Accepted, response);
|
|
|
|
|
break;
|
2023-08-31 12:16:02 +02:00
|
|
|
|
case "Jobs/DownloadNewChapters":
|
2023-08-27 01:01:39 +02:00
|
|
|
|
if(!requestVariables.TryGetValue("connector", out connectorName) ||
|
|
|
|
|
!requestVariables.TryGetValue("internalId", out internalId) ||
|
|
|
|
|
_parent.GetConnector(connectorName) is null ||
|
|
|
|
|
_parent.GetPublicationById(internalId) is null)
|
|
|
|
|
{
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
connector = _parent.GetConnector(connectorName)!;
|
2023-08-31 12:16:02 +02:00
|
|
|
|
manga = (Manga)_parent.GetPublicationById(internalId)!;
|
2023-09-01 23:37:50 +02:00
|
|
|
|
_parent.jobBoss.RemoveJobs(_parent.jobBoss.GetJobsLike(connector, manga));
|
2023-08-27 01:01:39 +02:00
|
|
|
|
SendResponse(HttpStatusCode.Accepted, response);
|
|
|
|
|
break;
|
|
|
|
|
case "NotificationConnectors":
|
|
|
|
|
if (!requestVariables.TryGetValue("notificationConnector", out string? notificationConnectorStr) ||
|
2023-08-31 16:02:02 +02:00
|
|
|
|
!Enum.TryParse(notificationConnectorStr, out NotificationConnector.NotificationConnectorType notificationConnectorType))
|
2023-08-27 01:01:39 +02:00
|
|
|
|
{
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-08-31 16:02:02 +02:00
|
|
|
|
DeleteNotificationConnector(notificationConnectorType);
|
2023-08-27 01:01:39 +02:00
|
|
|
|
SendResponse(HttpStatusCode.Accepted, response);
|
|
|
|
|
break;
|
2023-08-31 16:02:02 +02:00
|
|
|
|
case "LibraryConnectors":
|
|
|
|
|
if (!requestVariables.TryGetValue("libraryConnectors", out string? libraryConnectorStr) ||
|
|
|
|
|
!Enum.TryParse(libraryConnectorStr,
|
|
|
|
|
out LibraryConnector.LibraryType libraryConnectoryType))
|
2023-08-27 01:01:39 +02:00
|
|
|
|
{
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-08-31 16:02:02 +02:00
|
|
|
|
DeleteLibraryConnector(libraryConnectoryType);
|
2023-08-27 01:01:39 +02:00
|
|
|
|
SendResponse(HttpStatusCode.Accepted, response);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
SendResponse(HttpStatusCode.BadRequest, response);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-08-24 13:35:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SendResponse(HttpStatusCode statusCode, HttpListenerResponse response, object? content = null)
|
|
|
|
|
{
|
2023-09-02 14:11:11 +02:00
|
|
|
|
//Log($"Response: {statusCode} {content}");
|
2023-08-24 13:35:07 +02:00
|
|
|
|
response.StatusCode = (int)statusCode;
|
|
|
|
|
response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");
|
|
|
|
|
response.AddHeader("Access-Control-Allow-Methods", "GET, POST, DELETE");
|
|
|
|
|
response.AddHeader("Access-Control-Max-Age", "1728000");
|
|
|
|
|
response.AppendHeader("Access-Control-Allow-Origin", "*");
|
2023-09-01 21:40:56 +02:00
|
|
|
|
|
|
|
|
|
if (content is not FileStream stream)
|
2023-08-24 13:35:07 +02:00
|
|
|
|
{
|
2023-09-01 21:40:56 +02:00
|
|
|
|
response.ContentType = "application/json";
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
response.OutputStream.Write(content is not null
|
|
|
|
|
? Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(content))
|
|
|
|
|
: Array.Empty<byte>());
|
|
|
|
|
response.OutputStream.Close();
|
|
|
|
|
}
|
|
|
|
|
catch (HttpListenerException e)
|
|
|
|
|
{
|
|
|
|
|
Log(e.ToString());
|
|
|
|
|
}
|
2023-08-24 13:35:07 +02:00
|
|
|
|
}
|
2023-09-01 21:40:56 +02:00
|
|
|
|
else
|
2023-08-24 13:35:07 +02:00
|
|
|
|
{
|
2023-09-01 21:40:56 +02:00
|
|
|
|
stream.CopyTo(response.OutputStream);
|
|
|
|
|
response.OutputStream.Close();
|
|
|
|
|
stream.Close();
|
2023-08-24 13:35:07 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|