mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-09 03:04:19 -04:00
Sentry Integration (#212)
* Fixed a parsing case * Integrated Sentry into the solution with anonymous users. Fixed some parsing issues and added BuildInfo into a separate project. * Fixed some bad parser regex * Removed bad reference to NLog * Cleanup of some files not needed
This commit is contained in:
parent
6fc5e535df
commit
c8adaee3eb
33
.github/workflows/nightly-docker.yml
vendored
Normal file
33
.github/workflows/nightly-docker.yml
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
name: ci
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v1
|
||||||
|
-
|
||||||
|
name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
-
|
||||||
|
name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
-
|
||||||
|
name: Build and push
|
||||||
|
id: docker_build
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
push: true
|
||||||
|
tags: user/app:latest
|
||||||
|
-
|
||||||
|
name: Image digest
|
||||||
|
run: echo ${{ steps.docker_build.outputs.digest }}
|
@ -61,7 +61,6 @@ namespace API.Tests.Parser
|
|||||||
[InlineData("[Hidoi]_Amaenaideyo_MS_vol01_chp02.rar", "1")]
|
[InlineData("[Hidoi]_Amaenaideyo_MS_vol01_chp02.rar", "1")]
|
||||||
[InlineData("NEEDLESS_Vol.4_-_Simeon_6_v2_[SugoiSugoi].rar", "4")]
|
[InlineData("NEEDLESS_Vol.4_-_Simeon_6_v2_[SugoiSugoi].rar", "4")]
|
||||||
[InlineData("Okusama wa Shougakusei c003 (v01) [bokuwaNEET]", "1")]
|
[InlineData("Okusama wa Shougakusei c003 (v01) [bokuwaNEET]", "1")]
|
||||||
|
|
||||||
public void ParseVolumeTest(string filename, string expected)
|
public void ParseVolumeTest(string filename, string expected)
|
||||||
{
|
{
|
||||||
Assert.Equal(expected, API.Parser.Parser.ParseVolume(filename));
|
Assert.Equal(expected, API.Parser.Parser.ParseVolume(filename));
|
||||||
@ -137,6 +136,7 @@ namespace API.Tests.Parser
|
|||||||
[InlineData("Okusama wa Shougakusei c003 (v01) [bokuwaNEET]", "Okusama wa Shougakusei")]
|
[InlineData("Okusama wa Shougakusei c003 (v01) [bokuwaNEET]", "Okusama wa Shougakusei")]
|
||||||
[InlineData("VanDread-v01-c001[MD].zip", "VanDread")]
|
[InlineData("VanDread-v01-c001[MD].zip", "VanDread")]
|
||||||
[InlineData("Momo The Blood Taker - Chapter 027 Violent Emotion.cbz", "Momo The Blood Taker")]
|
[InlineData("Momo The Blood Taker - Chapter 027 Violent Emotion.cbz", "Momo The Blood Taker")]
|
||||||
|
[InlineData("Green Worldz - Chapter 112 Final Chapter (End).cbz", "Green Worldz")]
|
||||||
public void ParseSeriesTest(string filename, string expected)
|
public void ParseSeriesTest(string filename, string expected)
|
||||||
{
|
{
|
||||||
Assert.Equal(expected, API.Parser.Parser.ParseSeries(filename));
|
Assert.Equal(expected, API.Parser.Parser.ParseSeries(filename));
|
||||||
@ -225,6 +225,7 @@ namespace API.Tests.Parser
|
|||||||
[InlineData("Corpse Party -The Anthology- Sachikos game of love Hysteric Birthday 2U Extra Chapter", true)]
|
[InlineData("Corpse Party -The Anthology- Sachikos game of love Hysteric Birthday 2U Extra Chapter", true)]
|
||||||
[InlineData("Ani-Hina Art Collection.cbz", true)]
|
[InlineData("Ani-Hina Art Collection.cbz", true)]
|
||||||
[InlineData("Gifting The Wonderful World With Blessings! - 3 Side Stories [yuNS][Unknown]", true)]
|
[InlineData("Gifting The Wonderful World With Blessings! - 3 Side Stories [yuNS][Unknown]", true)]
|
||||||
|
[InlineData("Yuki Merry - 4-Komga Anthology", true)]
|
||||||
public void ParseMangaSpecialTest(string input, bool expected)
|
public void ParseMangaSpecialTest(string input, bool expected)
|
||||||
{
|
{
|
||||||
Assert.Equal(expected, !string.IsNullOrEmpty(API.Parser.Parser.ParseMangaSpecial(input)));
|
Assert.Equal(expected, !string.IsNullOrEmpty(API.Parser.Parser.ParseMangaSpecial(input)));
|
||||||
|
@ -12,6 +12,23 @@
|
|||||||
<ApplicationIcon>../favicon.ico</ApplicationIcon>
|
<ApplicationIcon>../favicon.ico</ApplicationIcon>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- Set the Product and Version info for our own projects -->
|
||||||
|
<PropertyGroup>
|
||||||
|
<Product>Kavita</Product>
|
||||||
|
<Company>kareadita.github.io</Company>
|
||||||
|
<Copyright>Copyright 2020-$([System.DateTime]::Now.ToString('yyyy')) kareadita.github.io (GNU General Public v3)</Copyright>
|
||||||
|
|
||||||
|
<!-- Should be replaced by CI -->
|
||||||
|
<AssemblyVersion>0.4.1</AssemblyVersion>
|
||||||
|
<AssemblyConfiguration>$(Configuration)-dev</AssemblyConfiguration>
|
||||||
|
|
||||||
|
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
|
||||||
|
<GenerateAssemblyInformationalVersionAttribute>false</GenerateAssemblyInformationalVersionAttribute>
|
||||||
|
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||||
|
|
||||||
|
<Deterministic Condition="$(AssemblyVersion.EndsWith('*'))">False</Deterministic>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1" />
|
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1" />
|
||||||
<PackageReference Include="ExCSS" Version="4.1.0" />
|
<PackageReference Include="ExCSS" Version="4.1.0" />
|
||||||
@ -33,6 +50,7 @@
|
|||||||
<PackageReference Include="NetVips" Version="2.0.0" />
|
<PackageReference Include="NetVips" Version="2.0.0" />
|
||||||
<PackageReference Include="NetVips.Native" Version="8.10.6" />
|
<PackageReference Include="NetVips.Native" Version="8.10.6" />
|
||||||
<PackageReference Include="NReco.Logging.File" Version="1.1.1" />
|
<PackageReference Include="NReco.Logging.File" Version="1.1.1" />
|
||||||
|
<PackageReference Include="Sentry.AspNetCore" Version="3.3.4" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.28.1" />
|
<PackageReference Include="SharpCompress" Version="0.28.1" />
|
||||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.20.0.28934">
|
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.20.0.28934">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@ -65,4 +83,8 @@
|
|||||||
<_ContentIncludedByDefault Remove="logs\kavita.json" />
|
<_ContentIncludedByDefault Remove="logs\kavita.json" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Kavita.Common\Kavita.Common.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using API.DTOs;
|
using API.DTOs;
|
||||||
using API.Entities;
|
using API.Entities;
|
||||||
@ -148,6 +149,7 @@ namespace API.Controllers
|
|||||||
public async Task<ActionResult<IEnumerable<SeriesDto>>> GetRecentlyAdded(int libraryId = 0, int limit = 20)
|
public async Task<ActionResult<IEnumerable<SeriesDto>>> GetRecentlyAdded(int libraryId = 0, int limit = 20)
|
||||||
{
|
{
|
||||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
|
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
|
||||||
|
if (user == null) return Ok(Array.Empty<SeriesDto>());
|
||||||
return Ok(await _unitOfWork.SeriesRepository.GetRecentlyAdded(user.Id, libraryId, limit));
|
return Ok(await _unitOfWork.SeriesRepository.GetRecentlyAdded(user.Id, libraryId, limit));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,6 +157,7 @@ namespace API.Controllers
|
|||||||
public async Task<ActionResult<IEnumerable<SeriesDto>>> GetInProgress(int libraryId = 0, int limit = 20)
|
public async Task<ActionResult<IEnumerable<SeriesDto>>> GetInProgress(int libraryId = 0, int limit = 20)
|
||||||
{
|
{
|
||||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
|
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
|
||||||
|
if (user == null) return Ok(Array.Empty<SeriesDto>());
|
||||||
return Ok(await _unitOfWork.SeriesRepository.GetInProgress(user.Id, libraryId, limit));
|
return Ok(await _unitOfWork.SeriesRepository.GetInProgress(user.Id, libraryId, limit));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,20 +1,40 @@
|
|||||||
FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
|
#This Dockerfile pulls the latest git commit and builds Kavita from source
|
||||||
WORKDIR /app
|
FROM mcr.microsoft.com/dotnet/sdk:5.0-focal AS builder
|
||||||
EXPOSE 80
|
|
||||||
EXPOSE 443
|
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
WORKDIR /src
|
ARG TARGETPLATFORM
|
||||||
COPY ["API/API.csproj", "API/"]
|
|
||||||
RUN dotnet restore "API/API.csproj"
|
|
||||||
COPY . .
|
|
||||||
WORKDIR "/src/API"
|
|
||||||
RUN dotnet build "API.csproj" -c Release -o /app/build
|
|
||||||
|
|
||||||
FROM build AS publish
|
#Installs nodejs and npm
|
||||||
RUN dotnet publish "API.csproj" -c Release -o /app/publish
|
RUN curl -fsSL https://deb.nodesource.com/setup_14.x | bash - \
|
||||||
|
&& apt-get install -y nodejs \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
FROM base AS final
|
#Builds app based on platform
|
||||||
WORKDIR /app
|
COPY build_target.sh /build_target.sh
|
||||||
COPY --from=publish /app/publish .
|
RUN /build_target.sh
|
||||||
ENTRYPOINT ["dotnet", "API.dll"]
|
|
||||||
|
#Production image
|
||||||
|
FROM ubuntu:focal
|
||||||
|
|
||||||
|
#Move the output files to where they need to be
|
||||||
|
COPY --from=builder /Projects/Kavita/_output/build/Kavita /kavita
|
||||||
|
|
||||||
|
#Installs program dependencies
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y libicu-dev libssl1.1 pwgen \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
#Creates the manga storage directory
|
||||||
|
RUN mkdir /manga /kavita/data
|
||||||
|
|
||||||
|
RUN cp /kavita/appsettings.Development.json /kavita/appsettings.json \
|
||||||
|
&& sed -i 's/Data source=kavita.db/Data source=data\/kavita.db/g' /kavita/appsettings.json
|
||||||
|
|
||||||
|
COPY entrypoint.sh /entrypoint.sh
|
||||||
|
|
||||||
|
EXPOSE 5000
|
||||||
|
|
||||||
|
WORKDIR /kavita
|
||||||
|
|
||||||
|
ENTRYPOINT ["/bin/bash"]
|
||||||
|
CMD ["/entrypoint.sh"]
|
@ -92,7 +92,7 @@ namespace API.Parser
|
|||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||||
// Momo The Blood Taker - Chapter 027 Violent Emotion.cbz
|
// Momo The Blood Taker - Chapter 027 Violent Emotion.cbz
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*) (\b|_|-)(?:chapter)",
|
@"(?<Series>.*)(\b|_|-|\s)(?:chapter)(\b|_|-|\s)\d",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||||
// Historys Strongest Disciple Kenichi_v11_c90-98.zip, Killing Bites Vol. 0001 Ch. 0001 - Galactica Scanlations (gb)
|
// Historys Strongest Disciple Kenichi_v11_c90-98.zip, Killing Bites Vol. 0001 Ch. 0001 - Galactica Scanlations (gb)
|
||||||
new Regex(
|
new Regex(
|
||||||
@ -101,7 +101,7 @@ namespace API.Parser
|
|||||||
//Ichinensei_ni_Nacchattara_v01_ch01_[Taruby]_v1.1.zip must be before [Suihei Kiki]_Kasumi_Otoko_no_Ko_[Taruby]_v1.1.zip
|
//Ichinensei_ni_Nacchattara_v01_ch01_[Taruby]_v1.1.zip must be before [Suihei Kiki]_Kasumi_Otoko_no_Ko_[Taruby]_v1.1.zip
|
||||||
// due to duplicate version identifiers in file.
|
// due to duplicate version identifiers in file.
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(v|s)\d+(-\d+)?(_| )",
|
@"(?<Series>.*)(v|s)\d+(-\d+)?(_|\s)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||||
//[Suihei Kiki]_Kasumi_Otoko_no_Ko_[Taruby]_v1.1.zip
|
//[Suihei Kiki]_Kasumi_Otoko_no_Ko_[Taruby]_v1.1.zip
|
||||||
new Regex(
|
new Regex(
|
||||||
@ -121,7 +121,7 @@ namespace API.Parser
|
|||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||||
// Tonikaku Kawaii (Ch 59-67) (Ongoing)
|
// Tonikaku Kawaii (Ch 59-67) (Ongoing)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)( |_)\((c |ch |chapter )",
|
@"(?<Series>.*)(\s|_)\((c\s|ch\s|chapter\s)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||||
// Black Bullet (This is very loose, keep towards bottom)
|
// Black Bullet (This is very loose, keep towards bottom)
|
||||||
new Regex(
|
new Regex(
|
||||||
@ -364,7 +364,7 @@ namespace API.Parser
|
|||||||
{
|
{
|
||||||
// All Keywords, does not account for checking if contains volume/chapter identification. Parser.Parse() will handle.
|
// All Keywords, does not account for checking if contains volume/chapter identification. Parser.Parse() will handle.
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Special>Specials?|OneShot|One\-Shot|Omake|Extra( Chapter)?|Art Collection|Side( |_)Stories)",
|
@"(?<Special>Specials?|OneShot|One\-Shot|Omake|Extra( Chapter)?|Art Collection|Side( |_)Stories|(?<!The\s)Anthology)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using API.Data;
|
using API.Data;
|
||||||
using API.Entities;
|
using API.Entities;
|
||||||
|
using Kavita.Common;
|
||||||
|
using Kavita.Common.EnvironmentInfo;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||||
@ -9,18 +13,22 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Sentry;
|
||||||
|
|
||||||
namespace API
|
namespace API
|
||||||
{
|
{
|
||||||
public class Program
|
public class Program
|
||||||
{
|
{
|
||||||
private static readonly int HttpPort = 5000;
|
private static readonly int HttpPort = 5000;
|
||||||
|
|
||||||
protected Program()
|
protected Program()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task Main(string[] args)
|
public static async Task Main(string[] args)
|
||||||
{
|
{
|
||||||
|
// Before anything, check if JWT has been generated properly or if user still has default
|
||||||
|
|
||||||
var host = CreateHostBuilder(args).Build();
|
var host = CreateHostBuilder(args).Build();
|
||||||
|
|
||||||
using var scope = host.Services.CreateScope();
|
using var scope = host.Services.CreateScope();
|
||||||
@ -55,6 +63,36 @@ namespace API
|
|||||||
options.Protocols = HttpProtocols.Http1AndHttp2;
|
options.Protocols = HttpProtocols.Http1AndHttp2;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
webBuilder.UseSentry(options =>
|
||||||
|
{
|
||||||
|
options.Dsn = "https://40f4e7b49c094172a6f99d61efb2740f@o641015.ingest.sentry.io/5757423";
|
||||||
|
options.MaxBreadcrumbs = 200;
|
||||||
|
options.AttachStacktrace = true;
|
||||||
|
options.Debug = false;
|
||||||
|
options.SendDefaultPii = false;
|
||||||
|
options.DiagnosticLevel = SentryLevel.Debug;
|
||||||
|
options.ShutdownTimeout = TimeSpan.FromSeconds(5);
|
||||||
|
options.Release = BuildInfo.Version.ToString();
|
||||||
|
options.AddExceptionFilterForType<OutOfMemoryException>();
|
||||||
|
options.AddExceptionFilterForType<NetVips.VipsException>();
|
||||||
|
options.AddExceptionFilterForType<InvalidDataException>();
|
||||||
|
options.ConfigureScope(scope =>
|
||||||
|
{
|
||||||
|
scope.User = new User()
|
||||||
|
{
|
||||||
|
Id = HashUtil.AnonymousToken()
|
||||||
|
};
|
||||||
|
scope.Contexts.App.Name = BuildInfo.AppName;
|
||||||
|
scope.Contexts.App.Version = BuildInfo.Version.ToString();
|
||||||
|
scope.Contexts.App.StartTime = DateTime.UtcNow;
|
||||||
|
scope.Contexts.App.Hash = HashUtil.AnonymousToken();
|
||||||
|
scope.Contexts.App.Build = BuildInfo.Release;
|
||||||
|
scope.SetTag("culture", Thread.CurrentThread.CurrentCulture.Name);
|
||||||
|
scope.SetTag("branch", BuildInfo.Branch);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
webBuilder.UseStartup<Startup>();
|
webBuilder.UseStartup<Startup>();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using API.Extensions;
|
using API.Extensions;
|
||||||
using API.Interfaces;
|
using API.Interfaces;
|
||||||
using API.Middleware;
|
using API.Middleware;
|
||||||
using API.Services;
|
using API.Services;
|
||||||
using Hangfire;
|
using Hangfire;
|
||||||
using Hangfire.MemoryStorage;
|
using Hangfire.MemoryStorage;
|
||||||
|
using Kavita.Common.EnvironmentInfo;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
@ -131,7 +133,7 @@ namespace API
|
|||||||
applicationLifetime.ApplicationStopping.Register(OnShutdown);
|
applicationLifetime.ApplicationStopping.Register(OnShutdown);
|
||||||
applicationLifetime.ApplicationStarted.Register(() =>
|
applicationLifetime.ApplicationStarted.Register(() =>
|
||||||
{
|
{
|
||||||
Console.WriteLine("Kavita - v0.4.1");
|
Console.WriteLine("Kavita - v" + BuildInfo.Version);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Any services that should be bootstrapped go here
|
// Any services that should be bootstrapped go here
|
||||||
@ -140,10 +142,10 @@ namespace API
|
|||||||
|
|
||||||
private void OnShutdown()
|
private void OnShutdown()
|
||||||
{
|
{
|
||||||
Console.WriteLine("Server is shutting down. Going to dispose Hangfire");
|
Console.WriteLine("Server is shutting down. Please allow a few seconds to stop any background jobs...");
|
||||||
//this code is called when the application stops
|
|
||||||
TaskScheduler.Client.Dispose();
|
TaskScheduler.Client.Dispose();
|
||||||
System.Threading.Thread.Sleep(1000);
|
System.Threading.Thread.Sleep(1000);
|
||||||
|
Console.WriteLine("You may now close the application window.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
56
Kavita.Common/EnvironmentInfo/BuildInfo.cs
Normal file
56
Kavita.Common/EnvironmentInfo/BuildInfo.cs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Kavita.Common.EnvironmentInfo
|
||||||
|
{
|
||||||
|
public static class BuildInfo
|
||||||
|
{
|
||||||
|
static BuildInfo()
|
||||||
|
{
|
||||||
|
var assembly = Assembly.GetExecutingAssembly();
|
||||||
|
|
||||||
|
Version = assembly.GetName().Version;
|
||||||
|
|
||||||
|
var attributes = assembly.GetCustomAttributes(true);
|
||||||
|
|
||||||
|
Branch = "unknown";
|
||||||
|
|
||||||
|
var config = attributes.OfType<AssemblyConfigurationAttribute>().FirstOrDefault();
|
||||||
|
if (config != null)
|
||||||
|
{
|
||||||
|
Branch = config.Configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
Release = $"{Version}-{Branch}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string AppName { get; } = "Kavita";
|
||||||
|
|
||||||
|
public static Version Version { get; }
|
||||||
|
public static string Branch { get; }
|
||||||
|
public static string Release { get; }
|
||||||
|
|
||||||
|
public static DateTime BuildDateTime
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var fileLocation = Assembly.GetCallingAssembly().Location;
|
||||||
|
return new FileInfo(fileLocation).LastWriteTimeUtc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsDebug
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
148
Kavita.Common/EnvironmentInfo/IOsInfo.cs
Normal file
148
Kavita.Common/EnvironmentInfo/IOsInfo.cs
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Kavita.Common.EnvironmentInfo
|
||||||
|
{
|
||||||
|
public class OsInfo : IOsInfo
|
||||||
|
{
|
||||||
|
public static Os Os { get; }
|
||||||
|
|
||||||
|
public static bool IsNotWindows => !IsWindows;
|
||||||
|
public static bool IsLinux => Os == Os.Linux || Os == Os.LinuxMusl || Os == Os.Bsd;
|
||||||
|
public static bool IsOsx => Os == Os.Osx;
|
||||||
|
public static bool IsWindows => Os == Os.Windows;
|
||||||
|
|
||||||
|
// this needs to not be static so we can mock it
|
||||||
|
public bool IsDocker { get; }
|
||||||
|
|
||||||
|
public string Version { get; }
|
||||||
|
public string Name { get; }
|
||||||
|
public string FullName { get; }
|
||||||
|
|
||||||
|
static OsInfo()
|
||||||
|
{
|
||||||
|
var platform = Environment.OSVersion.Platform;
|
||||||
|
|
||||||
|
switch (platform)
|
||||||
|
{
|
||||||
|
case PlatformID.Win32NT:
|
||||||
|
{
|
||||||
|
Os = Os.Windows;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PlatformID.MacOSX:
|
||||||
|
case PlatformID.Unix:
|
||||||
|
{
|
||||||
|
Os = GetPosixFlavour();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public OsInfo(IEnumerable<IOsVersionAdapter> versionAdapters)
|
||||||
|
{
|
||||||
|
OsVersionModel osInfo = null;
|
||||||
|
|
||||||
|
foreach (var osVersionAdapter in versionAdapters.Where(c => c.Enabled))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
osInfo = osVersionAdapter.Read();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Couldn't get OS Version info: " + e.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (osInfo != null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (osInfo != null)
|
||||||
|
{
|
||||||
|
Name = osInfo.Name;
|
||||||
|
Version = osInfo.Version;
|
||||||
|
FullName = osInfo.FullName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Name = Os.ToString();
|
||||||
|
FullName = Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsLinux && File.Exists("/proc/1/cgroup") && File.ReadAllText("/proc/1/cgroup").Contains("/docker/"))
|
||||||
|
{
|
||||||
|
IsDocker = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Os GetPosixFlavour()
|
||||||
|
{
|
||||||
|
var output = RunAndCapture("uname", "-s");
|
||||||
|
|
||||||
|
if (output.StartsWith("Darwin"))
|
||||||
|
{
|
||||||
|
return Os.Osx;
|
||||||
|
}
|
||||||
|
else if (output.Contains("BSD"))
|
||||||
|
{
|
||||||
|
return Os.Bsd;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#if ISMUSL
|
||||||
|
return Os.LinuxMusl;
|
||||||
|
#else
|
||||||
|
return Os.Linux;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string RunAndCapture(string filename, string args)
|
||||||
|
{
|
||||||
|
var p = new Process
|
||||||
|
{
|
||||||
|
StartInfo =
|
||||||
|
{
|
||||||
|
FileName = filename,
|
||||||
|
Arguments = args,
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true,
|
||||||
|
RedirectStandardOutput = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
p.Start();
|
||||||
|
|
||||||
|
// To avoid deadlocks, always read the output stream first and then wait.
|
||||||
|
var output = p.StandardOutput.ReadToEnd();
|
||||||
|
p.WaitForExit(1000);
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IOsInfo
|
||||||
|
{
|
||||||
|
string Version { get; }
|
||||||
|
string Name { get; }
|
||||||
|
string FullName { get; }
|
||||||
|
|
||||||
|
bool IsDocker { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Os
|
||||||
|
{
|
||||||
|
Windows,
|
||||||
|
Linux,
|
||||||
|
Osx,
|
||||||
|
LinuxMusl,
|
||||||
|
Bsd
|
||||||
|
}
|
||||||
|
}
|
8
Kavita.Common/EnvironmentInfo/IOsVersionAdapter.cs
Normal file
8
Kavita.Common/EnvironmentInfo/IOsVersionAdapter.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace Kavita.Common.EnvironmentInfo
|
||||||
|
{
|
||||||
|
public interface IOsVersionAdapter
|
||||||
|
{
|
||||||
|
bool Enabled { get; }
|
||||||
|
OsVersionModel Read();
|
||||||
|
}
|
||||||
|
}
|
27
Kavita.Common/EnvironmentInfo/OsVersionModel.cs
Normal file
27
Kavita.Common/EnvironmentInfo/OsVersionModel.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
namespace Kavita.Common.EnvironmentInfo
|
||||||
|
{
|
||||||
|
public class OsVersionModel
|
||||||
|
{
|
||||||
|
public OsVersionModel(string name, string version, string fullName = null)
|
||||||
|
{
|
||||||
|
Name = Trim(name);
|
||||||
|
Version = Trim(version);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(fullName))
|
||||||
|
{
|
||||||
|
fullName = $"{Name} {Version}";
|
||||||
|
}
|
||||||
|
|
||||||
|
FullName = Trim(fullName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Trim(string source)
|
||||||
|
{
|
||||||
|
return source.Trim().Trim('"', '\'');
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
public string FullName { get; }
|
||||||
|
public string Version { get; }
|
||||||
|
}
|
||||||
|
}
|
37
Kavita.Common/HashUtil.cs
Normal file
37
Kavita.Common/HashUtil.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Kavita.Common
|
||||||
|
{
|
||||||
|
public static class HashUtil
|
||||||
|
{
|
||||||
|
public static string CalculateCrc(string input)
|
||||||
|
{
|
||||||
|
uint mCrc = 0xffffffff;
|
||||||
|
byte[] bytes = Encoding.UTF8.GetBytes(input);
|
||||||
|
foreach (byte myByte in bytes)
|
||||||
|
{
|
||||||
|
mCrc ^= (uint)myByte << 24;
|
||||||
|
for (var i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
if ((Convert.ToUInt32(mCrc) & 0x80000000) == 0x80000000)
|
||||||
|
{
|
||||||
|
mCrc = (mCrc << 1) ^ 0x04C11DB7;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mCrc <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"{mCrc:x8}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string AnonymousToken()
|
||||||
|
{
|
||||||
|
var seed = $"{Environment.ProcessorCount}_{Environment.OSVersion.Platform}_{Environment.MachineName}_{Environment.UserName}";
|
||||||
|
return HashUtil.CalculateCrc(seed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
Kavita.Common/Kavita.Common.csproj
Normal file
24
Kavita.Common/Kavita.Common.csproj
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
<Company>kareadita.github.io</Company>
|
||||||
|
<Product>Kavita</Product>
|
||||||
|
<AssemblyVersion>0.4.1</AssemblyVersion>
|
||||||
|
<NeutralLanguage>en</NeutralLanguage>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Sentry" Version="3.3.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="JetBrains.ReSharper.TestRunner.Merged, Version=1.3.1.55, Culture=neutral, PublicKeyToken=5c492ec4f3eccde3">
|
||||||
|
<HintPath>D:\Program Files\JetBrains\JetBrains Rider 2020.3.2\lib\ReSharperHost\TestRunner\netcoreapp2.0\JetBrains.ReSharper.TestRunner.Merged.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Win32.Registry, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<HintPath>..\..\..\..\..\..\..\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.5\Microsoft.Win32.Registry.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
14
Kavita.sln
14
Kavita.sln
@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "API", "API\API.csproj", "{1
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "API.Tests", "API.Tests\API.Tests.csproj", "{6F7910F2-1B95-4570-A490-519C8935B9D1}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "API.Tests", "API.Tests\API.Tests.csproj", "{6F7910F2-1B95-4570-A490-519C8935B9D1}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kavita.Common", "Kavita.Common\Kavita.Common.csproj", "{165A86F5-9E74-4C05-9305-A6F0BA32C9EE}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -44,5 +46,17 @@ Global
|
|||||||
{6F7910F2-1B95-4570-A490-519C8935B9D1}.Release|x64.Build.0 = Release|Any CPU
|
{6F7910F2-1B95-4570-A490-519C8935B9D1}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{6F7910F2-1B95-4570-A490-519C8935B9D1}.Release|x86.ActiveCfg = Release|Any CPU
|
{6F7910F2-1B95-4570-A490-519C8935B9D1}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{6F7910F2-1B95-4570-A490-519C8935B9D1}.Release|x86.Build.0 = Release|Any CPU
|
{6F7910F2-1B95-4570-A490-519C8935B9D1}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{165A86F5-9E74-4C05-9305-A6F0BA32C9EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{165A86F5-9E74-4C05-9305-A6F0BA32C9EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{165A86F5-9E74-4C05-9305-A6F0BA32C9EE}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{165A86F5-9E74-4C05-9305-A6F0BA32C9EE}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{165A86F5-9E74-4C05-9305-A6F0BA32C9EE}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{165A86F5-9E74-4C05-9305-A6F0BA32C9EE}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{165A86F5-9E74-4C05-9305-A6F0BA32C9EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{165A86F5-9E74-4C05-9305-A6F0BA32C9EE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{165A86F5-9E74-4C05-9305-A6F0BA32C9EE}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{165A86F5-9E74-4C05-9305-A6F0BA32C9EE}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{165A86F5-9E74-4C05-9305-A6F0BA32C9EE}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{165A86F5-9E74-4C05-9305-A6F0BA32C9EE}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
@ -10,12 +10,13 @@ your manga collection with your friends and family!
|
|||||||
|
|
||||||
|
|
||||||
## Goals:
|
## Goals:
|
||||||
* Serve up Manga (cbr, cbz, zip/rar, raw images) and Books (epub, mobi, azw, djvu, pdf)
|
* Serve up Manga/Webtoons/Comics (cbr, cbz, zip/rar, raw images) and Books (epub, mobi, azw, djvu, pdf)
|
||||||
* Provide Reader for Manga and Books (Light Novels) via web app that is responsive
|
* Provide Readers via web app that is responsive
|
||||||
* Provide customization themes (server installed) for web app
|
* Provide a dark theme for web app
|
||||||
* Provide hooks into metadata providers to fetch Manga data
|
* Provide hooks into metadata providers to fetch Manga data
|
||||||
* Metadata should allow for collections, want to read integration from 3rd party services, genres.
|
* Metadata should allow for collections, want to read integration from 3rd party services, genres.
|
||||||
* Ability to manage users, access, and ratings
|
* Ability to manage users, access, and ratings
|
||||||
|
* Ability to sync ratings and reviews to external services
|
||||||
|
|
||||||
## How to Build
|
## How to Build
|
||||||
- Ensure you've cloned Kavita-webui. You should have Projects/Kavita and Projects/Kavita-webui
|
- Ensure you've cloned Kavita-webui. You should have Projects/Kavita and Projects/Kavita-webui
|
||||||
|
Loading…
x
Reference in New Issue
Block a user