mirror of
https://github.com/miawinter98/just-short-it.git
synced 2024-11-22 08:19:54 +00:00
changed: migrated urls page to razor components
This commit is contained in:
parent
b3670b2046
commit
61fba41cd6
|
@ -1,17 +1,25 @@
|
||||||
@if (Message is not null) {
|
@if (Message is not null) {
|
||||||
<div class="alert @GetAlertTypeClass() rounded-sm">
|
<div class="alert @GetAlertTypeClass() rounded-sm p-2" aria-role="alert">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" />
|
||||||
</svg>
|
</svg>
|
||||||
<span>
|
<span>
|
||||||
@Message
|
@Message
|
||||||
</span>
|
</span>
|
||||||
<button class="btn btn-square btn-sm btn-ghost" onclick="this.parentElement.remove()">
|
<div class="flex items-center gap-2">
|
||||||
|
@if (LinkToCopy is not null) {
|
||||||
|
<button class="btn btn-sm"
|
||||||
|
onclick='navigator.clipboard.writeText("@LinkToCopy")'>
|
||||||
|
Copy
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
<button class="btn btn-square btn-sm btn-ghost" onclick="this.parentElement.parentElement.remove()">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5">
|
||||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd"/>
|
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd"/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
@ -19,6 +27,8 @@
|
||||||
public string? Message { get; set; }
|
public string? Message { get; set; }
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public AlertType Type { get; set; } = AlertType.Information;
|
public AlertType Type { get; set; } = AlertType.Information;
|
||||||
|
[Parameter]
|
||||||
|
public string? LinkToCopy { get; set; }
|
||||||
|
|
||||||
private string GetAlertTypeClass() => Type switch{
|
private string GetAlertTypeClass() => Type switch{
|
||||||
AlertType.Information => "alert-info",
|
AlertType.Information => "alert-info",
|
||||||
|
|
195
Components/Pages/Urls.razor
Normal file
195
Components/Pages/Urls.razor
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
@page "/urls"
|
||||||
|
@using Microsoft.AspNetCore.Authorization
|
||||||
|
@using Microsoft.Extensions.Caching.Distributed
|
||||||
|
@using System.ComponentModel.DataAnnotations
|
||||||
|
@using System.Text.RegularExpressions
|
||||||
|
@using System.Web
|
||||||
|
@using Humanizer
|
||||||
|
@using Microsoft.AspNetCore.WebUtilities
|
||||||
|
@attribute [Authorize]
|
||||||
|
|
||||||
|
@inject IDistributedCache Db
|
||||||
|
@inject IConfiguration Configuration
|
||||||
|
@inject NavigationManager Navigation
|
||||||
|
|
||||||
|
<PageTitle>Urls - Just Short It</PageTitle>
|
||||||
|
|
||||||
|
<div class="grid place-items-center h-full text-primary-content">
|
||||||
|
<section class="w-full md:max-w-lg">
|
||||||
|
<h1 class="text-3xl lg:text-5xl text-primary-content text-center mb-6">
|
||||||
|
Urls Administration
|
||||||
|
</h1>
|
||||||
|
<MessageComponent Message="@Message" Type="MessageType" LinkToCopy="@Link" />
|
||||||
|
|
||||||
|
<EditForm class="mb-6" method="post" id="inspect-form"
|
||||||
|
FormName="inspect" Model="Inspect" OnValidSubmit="Submit_Inspect">
|
||||||
|
<DataAnnotationsValidator />
|
||||||
|
<h2 class="text-xl lg:text-2xl mb-3">Inspect URL</h2>
|
||||||
|
|
||||||
|
<div class="join w-full">
|
||||||
|
<label class="join-item btn btn-outline no-animation text-primary-content border-white">
|
||||||
|
ID
|
||||||
|
</label>
|
||||||
|
<div class="join-item flex-1">
|
||||||
|
<InputText id="inspect_id" class="input input-bordered w-full border-white"
|
||||||
|
min="2" max="16" autocomplete="off"
|
||||||
|
@bind-Value="Inspect.Id" required />
|
||||||
|
</div>
|
||||||
|
<div class="join-item">
|
||||||
|
<button class="btn btn-primary border-1" type="submit">Inspect</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ValidationMessage For="() => Inspect"></ValidationMessage>
|
||||||
|
</EditForm>
|
||||||
|
|
||||||
|
<EditForm method="post" id="new-form"
|
||||||
|
FormName="new" Model="New" OnValidSubmit="Submit_New">
|
||||||
|
<DataAnnotationsValidator />
|
||||||
|
<h2 class="text-xl lg:text-2xl mb-3">New URL</h2>
|
||||||
|
|
||||||
|
<div class="form-control w-full">
|
||||||
|
<span class="label">
|
||||||
|
<label class="label-text text-primary-content">ID</label>
|
||||||
|
</span>
|
||||||
|
<InputText id="new_id" class="input input-bordered w-full"
|
||||||
|
min="2" max="16" autocomplete="off"
|
||||||
|
@bind-Value="New.Id" required />
|
||||||
|
<span class="label">
|
||||||
|
<ValidationMessage class="label-text-alt text-error" For="() => New.Id" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control w-full">
|
||||||
|
<div class="label">
|
||||||
|
<label class="label-text text-primary-content">Target</label>
|
||||||
|
</div>
|
||||||
|
<InputText id="new_url" class="input input-bordered w-full" type="url" @bind-Value="New.Url"
|
||||||
|
autocomplete="off" required />
|
||||||
|
<span class="label">
|
||||||
|
<ValidationMessage class="label-text-alt text-error" For="() => New.Url" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control w-full">
|
||||||
|
<div class="label">
|
||||||
|
<label class="label-text text-primary-content">Expiration</label>
|
||||||
|
</div>
|
||||||
|
<InputSelect id="new_expiration" class="select select-bordered w-full"
|
||||||
|
@bind-Value="New.RedirectExpiration" DisplayName="Expiration" required>
|
||||||
|
<option value="null" disabled selected>Select Expiration</option>
|
||||||
|
@foreach (var expiration in Enum.GetValues<Expiration>()) {
|
||||||
|
<option value="@expiration">@expiration.Humanize()</option>
|
||||||
|
}
|
||||||
|
</InputSelect>
|
||||||
|
<span class="label">
|
||||||
|
<ValidationMessage class="label-text-alt text-error" For="() => New.RedirectExpiration" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn btn-primary w-full" type="submit">Create</button>
|
||||||
|
</EditForm>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[SupplyParameterFromForm(FormName = "inspect")]
|
||||||
|
public InspectModel Inspect { get; set; } = default!;
|
||||||
|
[SupplyParameterFromForm(FormName = "new")]
|
||||||
|
public NewModel New { get; set; } = default!;
|
||||||
|
|
||||||
|
private string BaseUrl { get; set; } = null!;
|
||||||
|
private string? Message { get; set; }
|
||||||
|
private string? Link { get; set; }
|
||||||
|
private MessageComponent.AlertType MessageType { get; set; }
|
||||||
|
|
||||||
|
protected override void OnInitialized() {
|
||||||
|
// ReSharper disable NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract
|
||||||
|
Inspect ??= new InspectModel();
|
||||||
|
New ??= new NewModel();
|
||||||
|
// ReSharper restore NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract
|
||||||
|
New.Id ??= GenerateNewId();
|
||||||
|
|
||||||
|
string url = Configuration.GetValue<string>("BaseUrl") ?? throw new ApplicationException("BaseUrl not set");
|
||||||
|
BaseUrl = new Uri(url, UriKind.Absolute).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Submit_Inspect(EditContext context) {
|
||||||
|
if (string.IsNullOrWhiteSpace(Inspect.Id)) return;
|
||||||
|
|
||||||
|
if (await Db.GetAsync(Inspect.Id) is null) {
|
||||||
|
Message = "ID does not exist";
|
||||||
|
MessageType = MessageComponent.AlertType.Error;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Navigation.NavigateTo(QueryHelpers.AddQueryString("/inspect", "Id", Inspect.Id));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Submit_New() {
|
||||||
|
if (string.IsNullOrWhiteSpace(New.Id)) return;
|
||||||
|
|
||||||
|
string id = HttpUtility.UrlEncode(New.Id);
|
||||||
|
|
||||||
|
if (await Db.GetAsync(id) is not null) {
|
||||||
|
Message = "This ID is already taken, sorry!";
|
||||||
|
MessageType = MessageComponent.AlertType.Error;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Uri.TryCreate($"{BaseUrl}{id}", UriKind.Absolute, out var link) is false) {
|
||||||
|
Message = "This ID cannot be used in a URL, sorry!";
|
||||||
|
MessageType = MessageComponent.AlertType.Error;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Db.SetStringAsync(id, New.Url!, new DistributedCacheEntryOptions {
|
||||||
|
AbsoluteExpiration = DateTime.FromBinary(ToUnixTime(New.RedirectExpiration!.Value))
|
||||||
|
});
|
||||||
|
|
||||||
|
Message = $"URL Generated! {link}";
|
||||||
|
Link = link.ToString();
|
||||||
|
MessageType = MessageComponent.AlertType.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GenerateNewId() {
|
||||||
|
string base64Guid = Regex.Replace(
|
||||||
|
Convert.ToBase64String(Guid.NewGuid().ToByteArray()),
|
||||||
|
"[/+=]", "");
|
||||||
|
return base64Guid[..6];
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Models
|
||||||
|
|
||||||
|
public sealed class InspectModel {
|
||||||
|
[Required(AllowEmptyStrings = false, ErrorMessage = "Id is required.")]
|
||||||
|
[MinLength(2, ErrorMessage = "Id needs to be at least 2 characters long.")]
|
||||||
|
[MaxLength(16, ErrorMessage = "Id needs to be at maximum 16 characters long.")]
|
||||||
|
public string? Id { get; set; }
|
||||||
|
}
|
||||||
|
public sealed class NewModel {
|
||||||
|
[Required(AllowEmptyStrings = false, ErrorMessage = "Id is required.")]
|
||||||
|
[MinLength(2, ErrorMessage = "Id needs to be at least 2 characters long.")]
|
||||||
|
[MaxLength(16, ErrorMessage = "Id needs to be at maximum 16 characters long.")]
|
||||||
|
public string? Id { get; set; }
|
||||||
|
[Required(AllowEmptyStrings = false, ErrorMessage = "Target is required.")]
|
||||||
|
[Url(ErrorMessage = "Target needs to be a valid URL.")]
|
||||||
|
public string? Url { get; set; }
|
||||||
|
[Required(ErrorMessage = "Expiration is required.")]
|
||||||
|
public Expiration? RedirectExpiration { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Expiration {
|
||||||
|
OneDay, OneWeek, FourWeeks, OneMonth, OneYear, Never
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long ToUnixTime(Expiration expiration) => expiration switch{
|
||||||
|
Expiration.OneDay => DateTime.UtcNow.AddDays(1).ToBinary(),
|
||||||
|
Expiration.OneWeek => DateTime.UtcNow.AddDays(7).ToBinary(),
|
||||||
|
Expiration.FourWeeks => DateTime.UtcNow.AddDays(4*7).ToBinary(),
|
||||||
|
Expiration.OneYear => DateTime.UtcNow.AddYears(1).ToBinary(),
|
||||||
|
Expiration.Never => DateTime.UtcNow.AddYears(1000).ToBinary(),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(expiration), expiration, null)};
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
|
@ -9,6 +9,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" />
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" />
|
||||||
|
|
|
@ -1,96 +0,0 @@
|
||||||
@page
|
|
||||||
@model JustShortIt.Pages.UrlsModel
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Urls";
|
|
||||||
}
|
|
||||||
|
|
||||||
<div class="grid place-items-center h-full text-primary-content">
|
|
||||||
<div class="w-full md:max-w-lg">
|
|
||||||
@if (!string.IsNullOrEmpty(Model.Message)) {
|
|
||||||
<!--
|
|
||||||
btn-success
|
|
||||||
/\
|
|
||||||
this just tells tailwind to generate it, since new uses this class for the link copy button
|
|
||||||
I know there is a safe-class thingie in the config, but this was just easier bc it's only needed here
|
|
||||||
-->
|
|
||||||
<div class="alert alert-info rounded-sm">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" />
|
|
||||||
</svg>
|
|
||||||
<span>
|
|
||||||
@Html.Raw(Model.Message)
|
|
||||||
</span>
|
|
||||||
<button class="btn btn-square btn-sm btn-ghost" onclick="this.parentElement.remove()">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5">
|
|
||||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
<h1 class="text-2xl lg:text-4xl my-6 text-center">Urls Administration</h1>
|
|
||||||
|
|
||||||
<form class="mb-6" method="post" asp-page="Urls" asp-page-handler="Inspect">
|
|
||||||
<h2 class="text-xl lg:text-2xl mb-3">Inspect URL</h2>
|
|
||||||
|
|
||||||
<div class="join w-full">
|
|
||||||
<label class="join-item btn btn-outline no-animation text-primary-content border-white">ID</label>
|
|
||||||
<div class="join-item flex-1">
|
|
||||||
<input required type="text" class="input input-bordered w-full border-white" name="Inspect_Id" autocomplete="off" />
|
|
||||||
</div>
|
|
||||||
<div class="join-item">
|
|
||||||
<button class="btn btn-primary border-1" type="submit">Inspect</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span class="text-error">@ModelState["Inspect_Id"]?.Errors.FirstOrDefault()?.ErrorMessage</span>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<form method="post" asp-page-handler="New">
|
|
||||||
<h2 class="text-xl lg:text-2xl mb-3">New URL</h2>
|
|
||||||
|
|
||||||
<div class="form-control w-full">
|
|
||||||
<span class="label">
|
|
||||||
<label class="label-text text-primary-content" asp-for="Model!.Id">ID</label>
|
|
||||||
</span>
|
|
||||||
<input required class="input input-bordered w-full" type="text" asp-for="Model!.Id" autocomplete="off" />
|
|
||||||
<span class="label">
|
|
||||||
<span class="label-text-alt text-error" asp-validation-for="Model!.Id"></span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-control w-full">
|
|
||||||
<div class="label">
|
|
||||||
<label class="label-text text-primary-content" asp-for="Model!.Target">Target</label>
|
|
||||||
</div>
|
|
||||||
<input required class="input input-bordered w-full" type="url" asp-for="Model!.Target" autocomplete="off" />
|
|
||||||
<div class="label">
|
|
||||||
<span class="label-text-alt text-error" asp-validation-for="Model!.Target"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-control w-full">
|
|
||||||
<span class="label">
|
|
||||||
<label class="label-text text-primary-content" asp-for="Model!.ExpirationDate">Expiration</label>
|
|
||||||
</span>
|
|
||||||
@Html.DropDownListFor(m => m.Model!.ExpirationDate, new List<SelectListItem> {
|
|
||||||
new("1 Day", DateTime.UtcNow.AddDays(1).ToBinary().ToString()),
|
|
||||||
new("1 Week", DateTime.UtcNow.AddDays(7).ToBinary().ToString()),
|
|
||||||
new("4 Weeks", DateTime.UtcNow.AddDays(4 * 7).ToBinary().ToString()),
|
|
||||||
new("1 Year", DateTime.UtcNow.AddYears(1).ToBinary().ToString()),
|
|
||||||
new("Never", DateTime.UtcNow.AddYears(1000).ToBinary().ToString())
|
|
||||||
}, "Select Expiration", new {
|
|
||||||
@class = "select select-bordered w-full"
|
|
||||||
})
|
|
||||||
<div class="label">
|
|
||||||
<span class="label-text-alt text-error" asp-validation-for="Model!.ExpirationDate"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="text-error pb-3" asp-validation-summary="ModelOnly"></div>
|
|
||||||
|
|
||||||
<button class="btn btn-primary w-full" type="submit">Create</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -1,83 +0,0 @@
|
||||||
using JustShortIt.Model;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Web;
|
|
||||||
using Microsoft.AspNetCore.WebUtilities;
|
|
||||||
using Microsoft.Extensions.Caching.Distributed;
|
|
||||||
|
|
||||||
namespace JustShortIt.Pages;
|
|
||||||
|
|
||||||
[Authorize]
|
|
||||||
public class UrlsModel : PageModel {
|
|
||||||
[BindProperty]
|
|
||||||
public UrlRedirect? Model { get; set; }
|
|
||||||
[BindProperty(Name="message")]
|
|
||||||
public string? Message { get; set; }
|
|
||||||
|
|
||||||
private string BaseUrl { get; }
|
|
||||||
private IDistributedCache Db { get; }
|
|
||||||
|
|
||||||
public UrlsModel(IConfiguration configuration, IDistributedCache db) {
|
|
||||||
#if DEBUG
|
|
||||||
BaseUrl = "https://localhost/";
|
|
||||||
#else
|
|
||||||
string url = configuration.GetValue<string>("BaseUrl") ?? throw new ApplicationException("BaseUrl not set");
|
|
||||||
BaseUrl = new Uri(url, UriKind.Absolute).ToString();
|
|
||||||
#endif
|
|
||||||
Db = db;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IActionResult> OnPostInspectAsync() {
|
|
||||||
string? id = Request.Form["Inspect_Id"];
|
|
||||||
if (id is null || string.IsNullOrEmpty(id)) {
|
|
||||||
ModelState.AddModelError("Inspect_Id", "ID is a required field");
|
|
||||||
return Page();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await Db.GetAsync(id) is null) {
|
|
||||||
ModelState.AddModelError("Inspect_Id", "ID does not exist");
|
|
||||||
return Page();
|
|
||||||
}
|
|
||||||
|
|
||||||
return LocalRedirect(QueryHelpers.AddQueryString("~/inspect", "Id", id));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IActionResult> OnPostNewAsync() {
|
|
||||||
if (!ModelState.IsValid) return Page();
|
|
||||||
string id = HttpUtility.UrlEncode(Model.Id);
|
|
||||||
|
|
||||||
if (await Db.GetAsync(id) is not null) {
|
|
||||||
Message = "This ID is already taken, sorry!";
|
|
||||||
return Page();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Uri.TryCreate($"{BaseUrl}{id}", UriKind.Absolute, out Uri? link) is false) {
|
|
||||||
Message = "This ID cannot be used in a URL, sorry!";
|
|
||||||
return Page();
|
|
||||||
}
|
|
||||||
|
|
||||||
await Db.SetStringAsync(id, Model.Target, new DistributedCacheEntryOptions {
|
|
||||||
AbsoluteExpiration = DateTime.FromBinary(long.Parse(Model.ExpirationDate))
|
|
||||||
});
|
|
||||||
|
|
||||||
ModelState.Clear();
|
|
||||||
ModelState.SetModelValue(nameof(UrlRedirect.Id), GenerateNewId(), GenerateNewId());
|
|
||||||
|
|
||||||
Message = $"URL Generated! <a class=link href='{link}'>{link}</a>. " +
|
|
||||||
$"<button class='btn btn-sm btn-success' onclick='navigator.clipboard.writeText(\"{link}\")'>Copy</button>";
|
|
||||||
return OnGet(Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IActionResult OnGet(string message) {
|
|
||||||
Message = message;
|
|
||||||
Model = new UrlRedirect(GenerateNewId(), string.Empty, string.Empty);
|
|
||||||
return Page();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GenerateNewId() {
|
|
||||||
string base64Guid = Regex.Replace(Convert.ToBase64String(Guid.NewGuid().ToByteArray()), "[/+=]", ""); ;
|
|
||||||
return base64Guid[..6];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,5 +5,6 @@
|
||||||
"Default": "Information",
|
"Default": "Information",
|
||||||
"Microsoft.AspNetCore": "Warning"
|
"Microsoft.AspNetCore": "Warning"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"BaseUrl": "http://localhost"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue