From db73af3bdd802be00b51b9f6c5370aa99af2a847 Mon Sep 17 00:00:00 2001 From: Glax Date: Mon, 26 Aug 2024 10:38:45 +0200 Subject: [PATCH 1/6] Fix crash when outputstream closes before response could be sent. #227 --- Tranga/Server.cs | 60 ++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/Tranga/Server.cs b/Tranga/Server.cs index e2f19f0..73ab7b6 100644 --- a/Tranga/Server.cs +++ b/Tranga/Server.cs @@ -712,6 +712,10 @@ public class Server : GlobalBase private void SendResponse(HttpStatusCode statusCode, HttpListenerResponse response, object? content = null) { + if (response.OutputStream.CanWrite == false) + { + Log($"No response sent to request: Stream closed before response could be sent."); + } //Log($"Response: {statusCode} {content}"); response.StatusCode = (int)statusCode; response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With"); @@ -719,43 +723,43 @@ public class Server : GlobalBase response.AddHeader("Access-Control-Max-Age", "1728000"); response.AppendHeader("Access-Control-Allow-Origin", "*"); - if (content is not Stream) + try { - response.ContentType = "application/json"; - try + if (content is not Stream) { + response.ContentType = "application/json"; response.OutputStream.Write(content is not null ? Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(content)) : Array.Empty()); - response.OutputStream.Close(); } - catch (HttpListenerException e) + else if (content is FileStream stream) { - Log(e.ToString()); + string contentType = stream.Name.Split('.')[^1]; + switch (contentType.ToLower()) + { + case "gif": + response.ContentType = "image/gif"; + break; + case "png": + response.ContentType = "image/png"; + break; + case "jpg": + case "jpeg": + response.ContentType = "image/jpeg"; + break; + case "log": + response.ContentType = "text/plain"; + break; + } + + stream.CopyTo(response.OutputStream); + stream.Close(); } - } - else if(content is FileStream stream) - { - string contentType = stream.Name.Split('.')[^1]; - switch (contentType.ToLower()) - { - case "gif": - response.ContentType = "image/gif"; - break; - case "png": - response.ContentType = "image/png"; - break; - case "jpg": - case "jpeg": - response.ContentType = "image/jpeg"; - break; - case "log": - response.ContentType = "text/plain"; - break; - } - stream.CopyTo(response.OutputStream); response.OutputStream.Close(); - stream.Close(); + } + catch (HttpListenerException e) + { + Log(e.ToString()); } } } \ No newline at end of file From 6f3bba99b0f9963b08bca80cda2bf86c50783395 Mon Sep 17 00:00:00 2001 From: Glax Date: Mon, 26 Aug 2024 12:59:19 +0200 Subject: [PATCH 2/6] Fix Settings not returning as JSON --- Tranga/Server.cs | 2 +- Tranga/TrangaSettings.cs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Tranga/Server.cs b/Tranga/Server.cs index 935d466..8656758 100644 --- a/Tranga/Server.cs +++ b/Tranga/Server.cs @@ -198,7 +198,7 @@ public class Server : GlobalBase SendResponse(HttpStatusCode.OK, response, _parent.jobBoss.jobs.Where(jjob => jjob is DownloadNewChapters).OrderBy(jjob => ((DownloadNewChapters)jjob).manga.sortName)); break; case "Settings": - SendResponse(HttpStatusCode.OK, response, TrangaSettings.Serialize()); + SendResponse(HttpStatusCode.OK, response, TrangaSettings.AsJObject()); break; case "Settings/userAgent": SendResponse(HttpStatusCode.OK, response, TrangaSettings.userAgent); diff --git a/Tranga/TrangaSettings.cs b/Tranga/TrangaSettings.cs index e2b9390..ae5b91d 100644 --- a/Tranga/TrangaSettings.cs +++ b/Tranga/TrangaSettings.cs @@ -154,7 +154,7 @@ public static class TrangaSettings File.WriteAllText(settingsFilePath, Serialize()); } - public static string Serialize() + public static JObject AsJObject() { JObject jobj = new JObject(); jobj.Add("downloadLocation", JToken.FromObject(TrangaSettings.downloadLocation)); @@ -164,9 +164,11 @@ public static class TrangaSettings jobj.Add("aprilFoolsMode", JToken.FromObject(TrangaSettings.aprilFoolsMode)); jobj.Add("version", JToken.FromObject(TrangaSettings.version)); jobj.Add("requestLimits", JToken.FromObject(TrangaSettings.requestLimits)); - return jobj.ToString(); + return jobj; } + public static string Serialize() => AsJObject().ToString(); + public static void Deserialize(string serialized) { JObject jobj = JObject.Parse(serialized); From 14e33cc49687074ffa762566e879a886392971cc Mon Sep 17 00:00:00 2001 From: Glax Date: Mon, 26 Aug 2024 13:09:33 +0200 Subject: [PATCH 3/6] Fix Settings not loading on reload --- CLI/Program.cs | 2 ++ Tranga/TrangaArgs.cs | 2 ++ Tranga/TrangaSettings.cs | 11 ++++------- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CLI/Program.cs b/CLI/Program.cs index f927ea2..c349b33 100644 --- a/CLI/Program.cs +++ b/CLI/Program.cs @@ -49,6 +49,8 @@ internal sealed class TrangaCli : Command if(settings.workingDirectory is not null) TrangaSettings.LoadFromWorkingDirectory(settings.workingDirectory); + else + TrangaSettings.CreateOrUpdate(); if(settings.downloadLocation is not null) TrangaSettings.CreateOrUpdate(downloadDirectory: settings.downloadLocation); diff --git a/Tranga/TrangaArgs.cs b/Tranga/TrangaArgs.cs index 77dfea3..f643d4a 100644 --- a/Tranga/TrangaArgs.cs +++ b/Tranga/TrangaArgs.cs @@ -41,6 +41,8 @@ public partial class Tranga : GlobalBase if (wdp) TrangaSettings.LoadFromWorkingDirectory(workingDirectoryPath![0]); + else + TrangaSettings.CreateOrUpdate(); if(dlp) TrangaSettings.CreateOrUpdate(downloadDirectory: downloadLocationPath![0]); diff --git a/Tranga/TrangaSettings.cs b/Tranga/TrangaSettings.cs index ae5b91d..b945fe5 100644 --- a/Tranga/TrangaSettings.cs +++ b/Tranga/TrangaSettings.cs @@ -37,14 +37,9 @@ public static class TrangaSettings public static void LoadFromWorkingDirectory(string directory) { TrangaSettings.workingDirectory = directory; - if (!File.Exists(settingsFilePath)) - { - return; - } - else - { + if(File.Exists(settingsFilePath)) Deserialize(File.ReadAllText(settingsFilePath)); - } + else return; Directory.CreateDirectory(downloadLocation); Directory.CreateDirectory(workingDirectory); @@ -53,6 +48,8 @@ public static class TrangaSettings public static void CreateOrUpdate(string? downloadDirectory = null, string? pWorkingDirectory = null, int? pApiPortNumber = null, string? pUserAgent = null, bool? pAprilFoolsMode = null) { + if(pWorkingDirectory is null && File.Exists(settingsFilePath)) + LoadFromWorkingDirectory(workingDirectory); TrangaSettings.downloadLocation = downloadDirectory ?? TrangaSettings.downloadLocation; TrangaSettings.workingDirectory = pWorkingDirectory ?? TrangaSettings.workingDirectory; TrangaSettings.apiPortNumber = pApiPortNumber ?? TrangaSettings.apiPortNumber; From 8f51d223034c27e6f0e9d9ac7ed7e281472cc246 Mon Sep 17 00:00:00 2001 From: Glax Date: Mon, 26 Aug 2024 13:21:34 +0200 Subject: [PATCH 4/6] Fix try-block in Server.cs --- Tranga/Server.cs | 57 ++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/Tranga/Server.cs b/Tranga/Server.cs index 78f96de..4e387f1 100644 --- a/Tranga/Server.cs +++ b/Tranga/Server.cs @@ -713,45 +713,46 @@ public class Server : GlobalBase response.AddHeader("Access-Control-Max-Age", "1728000"); response.AppendHeader("Access-Control-Allow-Origin", "*"); - if (content is not Stream) + try { - response.ContentType = "application/json"; - response.AddHeader("Cache-Control", "no-store"); - try + if (content is not Stream) { + response.ContentType = "application/json"; + response.AddHeader("Cache-Control", "no-store"); response.OutputStream.Write(content is not null ? Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(content)) : Array.Empty()); response.OutputStream.Close(); } - catch (HttpListenerException e) + else if (content is FileStream stream) { - Log(e.ToString()); + string contentType = stream.Name.Split('.')[^1]; + response.AddHeader("Cache-Control", "max-age=600"); + switch (contentType.ToLower()) + { + case "gif": + response.ContentType = "image/gif"; + break; + case "png": + response.ContentType = "image/png"; + break; + case "jpg": + case "jpeg": + response.ContentType = "image/jpeg"; + break; + case "log": + response.ContentType = "text/plain"; + break; + } + + stream.CopyTo(response.OutputStream); + response.OutputStream.Close(); + stream.Close(); } } - else if(content is FileStream stream) + catch (HttpListenerException e) { - string contentType = stream.Name.Split('.')[^1]; - response.AddHeader("Cache-Control", "max-age=600"); - switch (contentType.ToLower()) - { - case "gif": - response.ContentType = "image/gif"; - break; - case "png": - response.ContentType = "image/png"; - break; - case "jpg": - case "jpeg": - response.ContentType = "image/jpeg"; - break; - case "log": - response.ContentType = "text/plain"; - break; - } - stream.CopyTo(response.OutputStream); - response.OutputStream.Close(); - stream.Close(); + Log(e.ToString()); } } } \ No newline at end of file From 73093ab86c3920b10cde4ff395e0b9857387dbba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 05:55:58 +0000 Subject: [PATCH 5/6] Bump docker/build-push-action from 6.6.1 to 6.7.0 Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.6.1 to 6.7.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.6.1...v6.7.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/docker-base.yml | 2 +- .github/workflows/docker-image-cuttingedge.yml | 2 +- .github/workflows/docker-image-dev.yml | 2 +- .github/workflows/docker-image-master.yml | 2 +- .github/workflows/docker-image-serverv2.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docker-base.yml b/.github/workflows/docker-base.yml index 7caba73..cedd6d2 100644 --- a/.github/workflows/docker-base.yml +++ b/.github/workflows/docker-base.yml @@ -31,7 +31,7 @@ jobs: # https://github.com/docker/build-push-action#multi-platform-image - name: Build and push base - uses: docker/build-push-action@v6.6.1 + uses: docker/build-push-action@v6.7.0 with: context: ./ file: ./Dockerfile-base diff --git a/.github/workflows/docker-image-cuttingedge.yml b/.github/workflows/docker-image-cuttingedge.yml index 712f2ff..b5b6c3a 100644 --- a/.github/workflows/docker-image-cuttingedge.yml +++ b/.github/workflows/docker-image-cuttingedge.yml @@ -33,7 +33,7 @@ jobs: # https://github.com/docker/build-push-action#multi-platform-image - name: Build and push API - uses: docker/build-push-action@v6.6.1 + uses: docker/build-push-action@v6.7.0 with: context: ./ file: ./Dockerfile diff --git a/.github/workflows/docker-image-dev.yml b/.github/workflows/docker-image-dev.yml index 36e7350..8750d13 100644 --- a/.github/workflows/docker-image-dev.yml +++ b/.github/workflows/docker-image-dev.yml @@ -33,7 +33,7 @@ jobs: # https://github.com/docker/build-push-action#multi-platform-image - name: Build and push API - uses: docker/build-push-action@v6.6.1 + uses: docker/build-push-action@v6.7.0 with: context: ./ file: ./Dockerfile diff --git a/.github/workflows/docker-image-master.yml b/.github/workflows/docker-image-master.yml index 5d9adf9..db2403c 100644 --- a/.github/workflows/docker-image-master.yml +++ b/.github/workflows/docker-image-master.yml @@ -33,7 +33,7 @@ jobs: # https://github.com/docker/build-push-action#multi-platform-image - name: Build and push API - uses: docker/build-push-action@v6.6.1 + uses: docker/build-push-action@v6.7.0 with: context: ./ file: ./Dockerfile diff --git a/.github/workflows/docker-image-serverv2.yml b/.github/workflows/docker-image-serverv2.yml index 4a3608b..f160d58 100644 --- a/.github/workflows/docker-image-serverv2.yml +++ b/.github/workflows/docker-image-serverv2.yml @@ -33,7 +33,7 @@ jobs: # https://github.com/docker/build-push-action#multi-platform-image - name: Build and push API - uses: docker/build-push-action@v6.6.1 + uses: docker/build-push-action@v6.7.0 with: context: ./ file: ./Dockerfile From a968f4328d345980d7b1d16c03a3232ec94474eb Mon Sep 17 00:00:00 2001 From: vonProteus Date: Sat, 31 Aug 2024 15:53:27 +0200 Subject: [PATCH 6/6] Added support for ARM --- .dockerignore | 4 +- .github/workflows/docker-base.yml | 43 ----------------- .github/workflows/docker-image-master.yml | 2 +- .gitignore | 5 +- Dockerfile | 47 ++++++++++++------- Dockerfile-base | 8 ---- .../MangaConnectors/ChromiumDownloadClient.cs | 32 +------------ docker-compose.local.yaml | 21 +++++++++ 8 files changed, 61 insertions(+), 101 deletions(-) delete mode 100644 .github/workflows/docker-base.yml delete mode 100644 Dockerfile-base create mode 100644 docker-compose.local.yaml diff --git a/.dockerignore b/.dockerignore index cd967fc..efcb815 100644 --- a/.dockerignore +++ b/.dockerignore @@ -22,4 +22,6 @@ **/secrets.dev.yaml **/values.dev.yaml LICENSE -README.md \ No newline at end of file +README.md +Manga +settings diff --git a/.github/workflows/docker-base.yml b/.github/workflows/docker-base.yml deleted file mode 100644 index cedd6d2..0000000 --- a/.github/workflows/docker-base.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Docker Image CI - -on: - workflow_dispatch: - -jobs: - - build: - - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - - # https://github.com/docker/setup-qemu-action#usage - - name: Set up QEMU - uses: docker/setup-qemu-action@v3.2.0 - - # https://github.com/marketplace/actions/docker-setup-buildx - - name: Set up Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v3.6.1 - - # https://github.com/docker/login-action#docker-hub - - name: Login to Docker Hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - # https://github.com/docker/build-push-action#multi-platform-image - - name: Build and push base - uses: docker/build-push-action@v6.7.0 - with: - context: ./ - file: ./Dockerfile-base - #platforms: linux/amd64,linux/arm64,linux/riscv64,linux/ppc64le,linux/s390x,linux/386,linux/mips64le,linux/mips64,linux/arm/v7,linux/arm/v6 - platforms: linux/amd64 - pull: true - push: true - tags: | - glax/tranga-base:latest \ No newline at end of file diff --git a/.github/workflows/docker-image-master.yml b/.github/workflows/docker-image-master.yml index db2403c..dd98c5e 100644 --- a/.github/workflows/docker-image-master.yml +++ b/.github/workflows/docker-image-master.yml @@ -38,7 +38,7 @@ jobs: context: ./ file: ./Dockerfile #platforms: linux/amd64,linux/arm64,linux/riscv64,linux/ppc64le,linux/s390x,linux/386,linux/mips64le,linux/mips64,linux/arm/v7,linux/arm/v6 - platforms: linux/amd64 + platforms: linux/amd64,linux/arm64 pull: true push: true tags: | diff --git a/.gitignore b/.gitignore index 456b786..d439ba7 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,7 @@ riderModule.iml /.idea cover.jpg cover.png -/.vscode \ No newline at end of file +/.vscode +/Manga +/settings +*.DotSettings.user \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 25d4758..e3938b3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,29 +1,42 @@ # syntax=docker/dockerfile:1 +ARG DOTNET=7.0 -FROM mcr.microsoft.com/dotnet/sdk:7.0 as build-env +FROM mcr.microsoft.com/dotnet/runtime:$DOTNET AS base +WORKDIR /publish +ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true +ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium +RUN apt-get update \ + && apt-get install -y libx11-6 libx11-xcb1 libatk1.0-0 libgtk-3-0 libcups2 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxrandr2 libgbm1 libpango-1.0-0 libcairo2 libasound2 libxshmfence1 libnss3 chromium \ + && apt-get autopurge -y \ + && apt-get autoclean -y + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET AS build-env WORKDIR /src -COPY CLI /src/CLI -COPY Tranga /src/Tranga -COPY Logging /src/Logging -COPY Tranga.sln /src -RUN dotnet restore /src/Tranga/Tranga.csproj -RUN dotnet publish -c Release -o /publish -FROM glax/tranga-base:latest as runtime +COPY Tranga.sln /src +COPY CLI/CLI.csproj /src/CLI/CLI.csproj +COPY Logging/Logging.csproj /src/Logging/Logging.csproj +COPY Tranga/Tranga.csproj /src/Tranga/Tranga.csproj +RUN dotnet restore /src/Tranga.sln + +COPY . /src/ +RUN dotnet publish -c Release -o /publish -maxcpucount:1 + +FROM base AS runtime EXPOSE 6531 ARG UNAME=tranga ARG UID=1000 ARG GID=1000 -RUN groupadd -g $GID -o $UNAME -RUN useradd -m -u $UID -g $GID -o -s /bin/bash $UNAME -RUN mkdir /usr/share/tranga-api -RUN mkdir /Manga -RUN chown 1000:1000 /usr/share/tranga-api -RUN chown 1000:1000 /Manga +RUN groupadd -g $GID -o $UNAME \ + && useradd -m -u $UID -g $GID -o -s /bin/bash $UNAME \ + && mkdir /usr/share/tranga-api \ + && mkdir /Manga \ + && chown 1000:1000 /usr/share/tranga-api \ + && chown 1000:1000 /Manga USER $UNAME WORKDIR /publish -COPY --from=build-env /publish . +COPY --chown=1000:1000 --from=build-env /publish . USER 0 -RUN chown 1000:1000 /publish -ENTRYPOINT ["dotnet", "/publish/Tranga.dll", "-f", "-c", "-l", "/usr/share/tranga-api/logs"] +ENTRYPOINT ["dotnet", "/publish/Tranga.dll"] +CMD ["-f", "-c", "-l", "/usr/share/tranga-api/logs"] \ No newline at end of file diff --git a/Dockerfile-base b/Dockerfile-base deleted file mode 100644 index 6c9c7f1..0000000 --- a/Dockerfile-base +++ /dev/null @@ -1,8 +0,0 @@ -# syntax=docker/dockerfile:1 -#FROM mcr.microsoft.com/dotnet/aspnet:7.0 as runtime -FROM mcr.microsoft.com/dotnet/runtime:7.0 as runtime -WORKDIR /publish -RUN apt-get update -RUN apt-get install -y libx11-6 libx11-xcb1 libatk1.0-0 libgtk-3-0 libcups2 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxrandr2 libgbm1 libpango-1.0-0 libcairo2 libasound2 libxshmfence1 libnss3 -RUN apt-get autopurge -y -RUN apt-get autoclean -y \ No newline at end of file diff --git a/Tranga/MangaConnectors/ChromiumDownloadClient.cs b/Tranga/MangaConnectors/ChromiumDownloadClient.cs index d7e6523..9042812 100644 --- a/Tranga/MangaConnectors/ChromiumDownloadClient.cs +++ b/Tranga/MangaConnectors/ChromiumDownloadClient.cs @@ -13,40 +13,12 @@ internal class ChromiumDownloadClient : DownloadClient private const int StartTimeoutMs = 30000; private readonly HttpDownloadClient _httpDownloadClient; - private async Task DownloadBrowser() + private async Task StartBrowser() { - BrowserFetcher browserFetcher = new (); - foreach(string rev in browserFetcher.LocalRevisions().Where(rev => rev != ChromiumVersion)) - browserFetcher.Remove(rev); - if (!browserFetcher.LocalRevisions().Contains(ChromiumVersion)) - { - Log("Downloading headless browser"); - DateTime last = DateTime.Now.Subtract(TimeSpan.FromSeconds(5)); - browserFetcher.DownloadProgressChanged += (_, args) => - { - double currentBytes = Convert.ToDouble(args.BytesReceived) / Convert.ToDouble(args.TotalBytesToReceive); - if (args.TotalBytesToReceive == args.BytesReceived) - Log("Browser downloaded."); - else if (DateTime.Now > last.AddSeconds(1)) - { - Log($"Browser download progress: {currentBytes:P2}"); - last = DateTime.Now; - } - - }; - if (!browserFetcher.CanDownloadAsync(ChromiumVersion).Result) - { - Log($"Can't download browser version {ChromiumVersion}"); - throw new Exception(); - } - await browserFetcher.DownloadAsync(ChromiumVersion); - } - Log($"Starting Browser. ({StartTimeoutMs}ms timeout)"); return await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true, - ExecutablePath = browserFetcher.GetExecutablePath(ChromiumVersion), Args = new [] { "--disable-gpu", "--disable-dev-shm-usage", @@ -58,7 +30,7 @@ internal class ChromiumDownloadClient : DownloadClient public ChromiumDownloadClient(GlobalBase clone) : base(clone) { - this.browser = DownloadBrowser().Result; + this.browser = StartBrowser().Result; _httpDownloadClient = new(this); } diff --git a/docker-compose.local.yaml b/docker-compose.local.yaml new file mode 100644 index 0000000..9c7ac8d --- /dev/null +++ b/docker-compose.local.yaml @@ -0,0 +1,21 @@ +version: '3' +services: + tranga-api: + build: + dockerfile: Dockerfile + context: . + container_name: tranga-api + volumes: + - ./Manga:/Manga + - ./settings:/usr/share/tranga-api + ports: + - "6531:6531" + restart: unless-stopped + tranga-website: + image: glax/tranga-website:latest + container_name: tranga-website + ports: + - "9555:80" + depends_on: + - tranga-api + restart: unless-stopped \ No newline at end of file