Web Links (#1983)
* Updated dependencies * Updated the default key to be 256 bits to meet security requirements. * Added basic implementation of web link resolving favicon. Needs lots more work and testing on all OSes. * Implemented ability to see links and click on them for an individual chapter. * Hooked up the ability to set Series web links. * Render out the web link * Refactored out the favicon so there is a backup in case it fails. Refactored the baseline image placeholders to be dark mode since that is the default. * Added Robbie's nice error weblink fallbacks.
This commit is contained in:
parent
23fde65a7b
commit
bd8a1821a7
37 changed files with 4272 additions and 80 deletions
|
@ -59,12 +59,12 @@
|
|||
<PackageReference Include="ExCSS" Version="4.1.0" />
|
||||
<PackageReference Include="Flurl" Version="3.0.7" />
|
||||
<PackageReference Include="Flurl.Http" Version="3.2.4" />
|
||||
<PackageReference Include="Hangfire" Version="1.7.34" />
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.7.34" />
|
||||
<PackageReference Include="Hangfire.InMemory" Version="0.3.7" />
|
||||
<PackageReference Include="Hangfire" Version="1.8.0" />
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.0" />
|
||||
<PackageReference Include="Hangfire.InMemory" Version="0.4.0" />
|
||||
<PackageReference Include="Hangfire.MaximumConcurrentExecutions" Version="1.1.0" />
|
||||
<PackageReference Include="Hangfire.MemoryStorage.Core" Version="1.4.0" />
|
||||
<PackageReference Include="Hangfire.Storage.SQLite" Version="0.3.3" />
|
||||
<PackageReference Include="Hangfire.Storage.SQLite" Version="0.3.4" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.46" />
|
||||
<PackageReference Include="MarkdownDeep.NET.Core" Version="1.5.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.5" />
|
||||
|
@ -83,24 +83,24 @@
|
|||
<PackageReference Include="NetVips.Native" Version="8.14.2" />
|
||||
<PackageReference Include="NReco.Logging.File" Version="1.1.6" />
|
||||
<PackageReference Include="Serilog" Version="2.12.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="6.1.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
|
||||
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.2.0-dev-00752" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="5.0.1" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="3.4.0" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="7.0.0" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="7.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.AspNetCore.SignalR" Version="0.4.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.SignalR.Core" Version="0.1.2" />
|
||||
<PackageReference Include="SharpCompress" Version="0.33.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" />
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.55.0.65544">
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.0.0.68202">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.6" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.29.0" />
|
||||
<PackageReference Include="System.IO.Abstractions" Version="19.2.11" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.30.1" />
|
||||
<PackageReference Include="System.IO.Abstractions" Version="19.2.22" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
|
||||
<PackageReference Include="VersOne.Epub" Version="3.3.0-alpha1" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Constants;
|
||||
|
@ -20,12 +21,14 @@ public class ImageController : BaseApiController
|
|||
{
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
private readonly IDirectoryService _directoryService;
|
||||
private readonly IImageService _imageService;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ImageController(IUnitOfWork unitOfWork, IDirectoryService directoryService)
|
||||
public ImageController(IUnitOfWork unitOfWork, IDirectoryService directoryService, IImageService imageService)
|
||||
{
|
||||
_unitOfWork = unitOfWork;
|
||||
_directoryService = directoryService;
|
||||
_imageService = imageService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -157,6 +160,42 @@ public class ImageController : BaseApiController
|
|||
return PhysicalFile(file.FullName, MimeTypeMap.GetMimeType(format), Path.GetFileName(file.FullName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the image associated with a web-link
|
||||
/// </summary>
|
||||
/// <param name="chapterId"></param>
|
||||
/// <param name="pageNum"></param>
|
||||
/// <param name="apiKey"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("web-link")]
|
||||
[ResponseCache(CacheProfileName = ResponseCacheProfiles.Month, VaryByQueryKeys = new []{"url", "apiKey"})]
|
||||
public async Task<ActionResult> GetBookmarkImage(string url, string apiKey)
|
||||
{
|
||||
var userId = await _unitOfWork.UserRepository.GetUserIdByApiKeyAsync(apiKey);
|
||||
if (userId == 0) return BadRequest();
|
||||
|
||||
// Check if the domain exists
|
||||
var domainFilePath = _directoryService.FileSystem.Path.Join(_directoryService.FaviconDirectory, ImageService.GetWebLinkFormat(url));
|
||||
if (!_directoryService.FileSystem.File.Exists(domainFilePath))
|
||||
{
|
||||
// We need to request the favicon and save it
|
||||
try
|
||||
{
|
||||
domainFilePath = _directoryService.FileSystem.Path.Join(_directoryService.FaviconDirectory,
|
||||
await _imageService.DownloadFaviconAsync(url));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return BadRequest("There was an issue fetching favicon for domain");
|
||||
}
|
||||
}
|
||||
|
||||
var file = new FileInfo(domainFilePath);
|
||||
var format = Path.GetExtension(file.FullName);
|
||||
|
||||
return PhysicalFile(file.FullName, MimeTypeMap.GetMimeType(format), Path.GetFileName(file.FullName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a temp coverupload image
|
||||
/// </summary>
|
||||
|
|
|
@ -93,4 +93,8 @@ public class ChapterDto : IHasReadTimeEstimate, IEntityDate
|
|||
public int MaxHoursToRead { get; set; }
|
||||
/// <inheritdoc cref="IHasReadTimeEstimate.AvgHoursToRead"/>
|
||||
public int AvgHoursToRead { get; set; }
|
||||
/// <summary>
|
||||
/// Comma-separated link of urls to external services that have some relation to the Chapter
|
||||
/// </summary>
|
||||
public string WebLinks { get; set; }
|
||||
}
|
||||
|
|
|
@ -58,6 +58,10 @@ public class SeriesMetadataDto
|
|||
/// Publication status of the Series
|
||||
/// </summary>
|
||||
public PublicationStatus PublicationStatus { get; set; }
|
||||
/// <summary>
|
||||
/// A comma-separated list of Urls
|
||||
/// </summary>
|
||||
public string WebLinks { get; set; }
|
||||
|
||||
public bool LanguageLocked { get; set; }
|
||||
public bool SummaryLocked { get; set; }
|
||||
|
|
|
@ -114,6 +114,13 @@ public sealed class DataContext : IdentityDbContext<AppUser, AppRole, int,
|
|||
builder.Entity<Library>()
|
||||
.Property(b => b.ManageReadingLists)
|
||||
.HasDefaultValue(true);
|
||||
|
||||
builder.Entity<Chapter>()
|
||||
.Property(b => b.WebLinks)
|
||||
.HasDefaultValue(string.Empty);
|
||||
builder.Entity<SeriesMetadata>()
|
||||
.Property(b => b.WebLinks)
|
||||
.HasDefaultValue(string.Empty);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ public class ComicInfo
|
|||
/// <summary>
|
||||
/// This is the link to where the data was scraped from
|
||||
/// </summary>
|
||||
/// <remarks>This can be comma-separated</remarks>
|
||||
public string Web { get; set; } = string.Empty;
|
||||
[System.ComponentModel.DefaultValueAttribute(0)]
|
||||
public int Day { get; set; } = 0;
|
||||
|
|
1917
API/Data/Migrations/20230511165427_WebLinksForChapter.Designer.cs
generated
Normal file
1917
API/Data/Migrations/20230511165427_WebLinksForChapter.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
29
API/Data/Migrations/20230511165427_WebLinksForChapter.cs
Normal file
29
API/Data/Migrations/20230511165427_WebLinksForChapter.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class WebLinksForChapter : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "WebLinks",
|
||||
table: "Chapter",
|
||||
type: "TEXT",
|
||||
nullable: true,
|
||||
defaultValue: string.Empty);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "WebLinks",
|
||||
table: "Chapter");
|
||||
}
|
||||
}
|
||||
}
|
1922
API/Data/Migrations/20230511183339_WebLinksForSeries.Designer.cs
generated
Normal file
1922
API/Data/Migrations/20230511183339_WebLinksForSeries.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
29
API/Data/Migrations/20230511183339_WebLinksForSeries.cs
Normal file
29
API/Data/Migrations/20230511183339_WebLinksForSeries.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class WebLinksForSeries : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "WebLinks",
|
||||
table: "SeriesMetadata",
|
||||
type: "TEXT",
|
||||
nullable: true,
|
||||
defaultValue: string.Empty);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "WebLinks",
|
||||
table: "SeriesMetadata");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -467,6 +467,11 @@ namespace API.Data.Migrations
|
|||
b.Property<int>("VolumeId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("WebLinks")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValue("");
|
||||
|
||||
b.Property<long>("WordCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
|
@ -831,6 +836,11 @@ namespace API.Data.Migrations
|
|||
b.Property<bool>("TranslatorLocked")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("WebLinks")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValue("");
|
||||
|
||||
b.Property<bool>("WriterLocked")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
|
|
|
@ -100,7 +100,10 @@ public class Chapter : IEntityDate, IHasReadTimeEstimate
|
|||
public int MaxHoursToRead { get; set; }
|
||||
/// <inheritdoc cref="IHasReadTimeEstimate"/>
|
||||
public int AvgHoursToRead { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Comma-separated link of urls to external services that have some relation to the Chapter
|
||||
/// </summary>
|
||||
public string WebLinks { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// All people attached at a Chapter level. Usually Comics will have different people per issue.
|
||||
|
@ -115,7 +118,6 @@ public class Chapter : IEntityDate, IHasReadTimeEstimate
|
|||
public ICollection<AppUserProgress> UserProgress { get; set; }
|
||||
|
||||
|
||||
|
||||
// Relationships
|
||||
public Volume Volume { get; set; } = null!;
|
||||
public int VolumeId { get; set; }
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.Interfaces;
|
||||
|
@ -43,6 +44,11 @@ public class SeriesMetadata : IHasConcurrencyToken
|
|||
/// </summary>
|
||||
public int MaxCount { get; set; } = 0;
|
||||
public PublicationStatus PublicationStatus { get; set; }
|
||||
/// <summary>
|
||||
/// A Comma-separated list of strings representing links from the series
|
||||
/// </summary>
|
||||
/// <remarks>This is not populated from Chapters of the Series</remarks>
|
||||
public string WebLinks { get; set; } = string.Empty;
|
||||
|
||||
// Locks
|
||||
public bool LanguageLocked { get; set; }
|
||||
|
|
|
@ -49,7 +49,7 @@ public class Program
|
|||
Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") != Environments.Development)
|
||||
{
|
||||
Log.Logger.Information("Generating JWT TokenKey for encrypting user sessions...");
|
||||
var rBytes = new byte[128];
|
||||
var rBytes = new byte[256];
|
||||
RandomNumberGenerator.Create().GetBytes(rBytes);
|
||||
Configuration.JwtToken = Convert.ToBase64String(rBytes).Replace("/", string.Empty);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ public interface IDirectoryService
|
|||
string TempDirectory { get; }
|
||||
string ConfigDirectory { get; }
|
||||
string SiteThemeDirectory { get; }
|
||||
string FaviconDirectory { get; }
|
||||
/// <summary>
|
||||
/// Original BookmarkDirectory. Only used for resetting directory. Use <see cref="ServerSettingKey.BackupDirectory"/> for actual path.
|
||||
/// </summary>
|
||||
|
@ -75,6 +76,7 @@ public class DirectoryService : IDirectoryService
|
|||
public string ConfigDirectory { get; }
|
||||
public string BookmarkDirectory { get; }
|
||||
public string SiteThemeDirectory { get; }
|
||||
public string FaviconDirectory { get; }
|
||||
private readonly ILogger<DirectoryService> _logger;
|
||||
private const RegexOptions MatchOptions = RegexOptions.Compiled | RegexOptions.IgnoreCase;
|
||||
|
||||
|
@ -98,6 +100,7 @@ public class DirectoryService : IDirectoryService
|
|||
ConfigDirectory = FileSystem.Path.Join(FileSystem.Directory.GetCurrentDirectory(), "config");
|
||||
BookmarkDirectory = FileSystem.Path.Join(FileSystem.Directory.GetCurrentDirectory(), "config", "bookmarks");
|
||||
SiteThemeDirectory = FileSystem.Path.Join(FileSystem.Directory.GetCurrentDirectory(), "config", "themes");
|
||||
FaviconDirectory = FileSystem.Path.Join(FileSystem.Directory.GetCurrentDirectory(), "config", "favicons");
|
||||
|
||||
ExistOrCreate(SiteThemeDirectory);
|
||||
ExistOrCreate(CoverImageDirectory);
|
||||
|
@ -105,6 +108,7 @@ public class DirectoryService : IDirectoryService
|
|||
ExistOrCreate(LogDirectory);
|
||||
ExistOrCreate(TempDirectory);
|
||||
ExistOrCreate(BookmarkDirectory);
|
||||
ExistOrCreate(FaviconDirectory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Flurl;
|
||||
using Flurl.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NetVips;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Formats.Png;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using Image = NetVips.Image;
|
||||
using Size = SixLabors.ImageSharp.Size;
|
||||
|
||||
namespace API.Services;
|
||||
|
||||
|
@ -49,6 +57,7 @@ public interface IImageService
|
|||
Task<string> ConvertToWebP(string filePath, string outputPath);
|
||||
|
||||
Task<bool> IsImage(string filePath);
|
||||
Task<string> DownloadFaviconAsync(string url);
|
||||
}
|
||||
|
||||
public class ImageService : IImageService
|
||||
|
@ -177,6 +186,36 @@ public class ImageService : IImageService
|
|||
return false;
|
||||
}
|
||||
|
||||
public async Task<string> DownloadFaviconAsync(string url)
|
||||
{
|
||||
// Parse the URL to get the domain (including subdomain)
|
||||
var uri = new Uri(url);
|
||||
var domain = uri.Host;
|
||||
var baseUrl = uri.Scheme + "://" + uri.Host;
|
||||
try
|
||||
{
|
||||
// Download the favicon.ico file using Flurl
|
||||
var faviconStream = await baseUrl
|
||||
.AppendPathSegment("favicon.ico")
|
||||
.AllowHttpStatus("2xx")
|
||||
.GetStreamAsync();
|
||||
|
||||
// Create the destination file path
|
||||
var filename = $"{domain}.png";
|
||||
using var icon = new Icon(faviconStream);
|
||||
using var bitmap = icon.ToBitmap();
|
||||
bitmap.Save(Path.Combine(_directoryService.FaviconDirectory, filename), ImageFormat.Png);
|
||||
|
||||
_logger.LogDebug("Favicon.png for {Domain} downloaded and saved successfully", domain);
|
||||
return filename;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error downloading favicon.png for ${Domain}", domain);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public string CreateThumbnailFromBase64(string encodedImage, string fileName, bool saveAsWebP = false, int thumbnailWidth = ThumbnailWidth)
|
||||
|
@ -257,6 +296,11 @@ public class ImageService : IImageService
|
|||
return $"thumbnail{chapterId}";
|
||||
}
|
||||
|
||||
public static string GetWebLinkFormat(string url)
|
||||
{
|
||||
return $"{new Uri(url).Host}.png";
|
||||
}
|
||||
|
||||
|
||||
public static string CreateMergedImage(List<string> coverImages, string dest)
|
||||
{
|
||||
|
|
|
@ -120,6 +120,16 @@ public class SeriesService : ISeriesService
|
|||
series.Metadata.LanguageLocked = true;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(updateSeriesMetadataDto.SeriesMetadata?.WebLinks))
|
||||
{
|
||||
series.Metadata.WebLinks = string.Join(",", updateSeriesMetadataDto.SeriesMetadata?.WebLinks
|
||||
.Split(",")
|
||||
.Where(s => !string.IsNullOrEmpty(s))
|
||||
.Select(s => s.Trim())!
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
series.Metadata.CollectionTags ??= new List<CollectionTag>();
|
||||
UpdateCollectionsList(updateSeriesMetadataDto.CollectionTags, series, allCollectionTags, (tag) =>
|
||||
{
|
||||
|
|
|
@ -120,6 +120,8 @@ public class BackupService : IBackupService
|
|||
await SendProgress(0.75F, "Copying themes");
|
||||
|
||||
CopyThemesToBackupDirectory(tempDirectory);
|
||||
await SendProgress(0.85F, "Copying favicons");
|
||||
CopyFaviconsToBackupDirectory(tempDirectory);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -141,6 +143,11 @@ public class BackupService : IBackupService
|
|||
_directoryService.CopyFilesToDirectory(files, _directoryService.FileSystem.Path.Join(tempDirectory, "logs"));
|
||||
}
|
||||
|
||||
private void CopyFaviconsToBackupDirectory(string tempDirectory)
|
||||
{
|
||||
_directoryService.CopyDirectoryToDirectory(_directoryService.FaviconDirectory, tempDirectory);
|
||||
}
|
||||
|
||||
private async Task CopyCoverImagesToBackupDirectory(string tempDirectory)
|
||||
{
|
||||
var outputTempDir = Path.Join(tempDirectory, "covers");
|
||||
|
|
|
@ -708,12 +708,19 @@ public class ProcessSeries : IProcessSeries
|
|||
chapter.StoryArcNumber = comicInfo.StoryArcNumber;
|
||||
}
|
||||
|
||||
|
||||
if (comicInfo.AlternateCount > 0)
|
||||
{
|
||||
chapter.AlternateCount = comicInfo.AlternateCount;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(comicInfo.Web))
|
||||
{
|
||||
chapter.WebLinks = string.Join(",", comicInfo.Web
|
||||
.Split(",")
|
||||
.Where(s => !string.IsNullOrEmpty(s))
|
||||
.Select(s => s.Trim())
|
||||
);
|
||||
}
|
||||
|
||||
if (comicInfo.Count > 0)
|
||||
{
|
||||
|
|
|
@ -49,13 +49,12 @@ public class TokenService : ITokenService
|
|||
|
||||
claims.AddRange(roles.Select(role => new Claim(Role, role)));
|
||||
|
||||
var creds = new SigningCredentials(_key, SecurityAlgorithms.HmacSha512Signature);
|
||||
|
||||
var credentials = new SigningCredentials(_key, SecurityAlgorithms.HmacSha512Signature);
|
||||
var tokenDescriptor = new SecurityTokenDescriptor()
|
||||
{
|
||||
Subject = new ClaimsIdentity(claims),
|
||||
Expires = DateTime.UtcNow.AddDays(14),
|
||||
SigningCredentials = creds
|
||||
SigningCredentials = credentials
|
||||
};
|
||||
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"TokenKey": "super secret unguessable key",
|
||||
"TokenKey": "super secret unguessable key that is longer because we require it",
|
||||
"Port": 5000,
|
||||
"IpAddresses": "",
|
||||
"BaseUrl": "/joe/"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue