💄 Restyled web pages
🧱 Add tailwindcss
This commit is contained in:
@ -2,157 +2,109 @@
|
||||
@model DysonNetwork.Sphere.Pages.Checkpoint.CheckpointPage
|
||||
|
||||
@{
|
||||
Layout = null;
|
||||
|
||||
ViewData["Title"] = "Security Checkpoint";
|
||||
var cfg = ViewData.Model.Configuration;
|
||||
var provider = cfg.GetSection("Captcha")["Provider"]?.ToLower();
|
||||
var apiKey = cfg.GetSection("Captcha")["ApiKey"];
|
||||
}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Solar Network Captcha</title>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Roboto+Mono&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<style>
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
background-color: #2d2d2d;
|
||||
font-family: "Roboto Mono", monospace;
|
||||
color: #c9d1d9;
|
||||
}
|
||||
|
||||
.parent {
|
||||
padding: 20px;
|
||||
max-width: 480px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 18px;
|
||||
font-weight: 300;
|
||||
color: #ffffff;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 20px;
|
||||
font-size: 11px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.footer-product {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.g-recaptcha {
|
||||
display: inline-block; /* Adjust as needed */
|
||||
}
|
||||
</style>
|
||||
@section Scripts {
|
||||
@switch (provider)
|
||||
{
|
||||
case "recaptcha":
|
||||
<script src="https://www.recaptcha.net/recaptcha/api.js" async defer></script>
|
||||
break;
|
||||
case "cloudflare":
|
||||
<script
|
||||
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
|
||||
async
|
||||
defer
|
||||
></script>
|
||||
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
|
||||
break;
|
||||
case "hcaptcha":
|
||||
<script src="https://js.hcaptcha.com/1/api.js" async defer></script>
|
||||
break;
|
||||
}
|
||||
</head>
|
||||
<body>
|
||||
<div class="parent">
|
||||
<div class="container">
|
||||
<h1>reCaptcha</h1>
|
||||
@switch (provider)
|
||||
{
|
||||
case "cloudflare":
|
||||
<div
|
||||
class="cf-turnstile"
|
||||
data-sitekey="@apiKey"
|
||||
data-callback="onSuccess"
|
||||
></div>
|
||||
break;
|
||||
case "recaptcha":
|
||||
<div
|
||||
class="g-recaptcha"
|
||||
data-sitekey="@apiKey"
|
||||
data-callback="onSuccess"
|
||||
></div>
|
||||
break;
|
||||
case "hcaptcha":
|
||||
<div
|
||||
class="h-captcha"
|
||||
data-sitekey="@apiKey"
|
||||
data-callback="onSuccess"
|
||||
></div>
|
||||
break;
|
||||
default:
|
||||
<p style="color: yellow;">Captcha provider not configured correctly.</p>
|
||||
break;
|
||||
}
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="footer-product">Solar Network Anti-Robot</div>
|
||||
<a
|
||||
href="https://solsynth.dev"
|
||||
style="color: #c9d1d9; text-decoration: none"
|
||||
>Solsynth LLC</a>
|
||||
© @DateTime.Now.Year<br/>
|
||||
Powered by
|
||||
@switch (provider)
|
||||
{
|
||||
case "cloudflare":
|
||||
<a href="https://www.cloudflare.com/turnstile/" style="color: #c9d1d9"
|
||||
>Cloudflare Turnstile</a>
|
||||
break;
|
||||
case "recaptcha":
|
||||
<a href="https://www.google.com/recaptcha/" style="color: #c9d1d9"
|
||||
>Google reCaptcha</a>
|
||||
break;
|
||||
default:
|
||||
<span>Nothing</span>
|
||||
break;
|
||||
}
|
||||
<br/>
|
||||
Hosted by
|
||||
<a
|
||||
href="https://github.com/Solsynth/DysonNetwork"
|
||||
style="color: #c9d1d9"
|
||||
>DysonNetwork.Sphere</a>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function getQueryParam(name) {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
return urlParams.get(name);
|
||||
}
|
||||
|
||||
function onSuccess(token) {
|
||||
window.parent.postMessage("captcha_tk=" + token, "*");
|
||||
const redirectUri = getQueryParam("redirect_uri");
|
||||
if (redirectUri) {
|
||||
window.location.href = `${redirectUri}?captcha_tk=${encodeURIComponent(token)}`;
|
||||
<script>
|
||||
function getQueryParam(name) {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
return urlParams.get(name);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
function onSuccess(token) {
|
||||
window.parent.postMessage("captcha_tk=" + token, "*");
|
||||
const redirectUri = getQueryParam("redirect_uri");
|
||||
if (redirectUri) {
|
||||
window.location.href = `${redirectUri}?captcha_tk=${encodeURIComponent(token)}`;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
}
|
||||
|
||||
<div class="h-full flex items-center justify-center">
|
||||
<div class="max-w-lg w-full mx-auto p-6">
|
||||
<div class="text-center">
|
||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">Security Check</h1>
|
||||
<p class="mb-6 text-gray-900 dark:text-white">Please complete the contest below to confirm you're not a robot</p>
|
||||
|
||||
<div class="flex justify-center mb-8">
|
||||
@switch (provider)
|
||||
{
|
||||
case "cloudflare":
|
||||
<div class="cf-turnstile"
|
||||
data-sitekey="@apiKey"
|
||||
data-callback="onSuccess">
|
||||
</div>
|
||||
break;
|
||||
case "recaptcha":
|
||||
<div class="g-recaptcha"
|
||||
data-sitekey="@apiKey"
|
||||
data-callback="onSuccess">
|
||||
</div>
|
||||
break;
|
||||
case "hcaptcha":
|
||||
<div class="h-captcha"
|
||||
data-sitekey="@apiKey"
|
||||
data-callback="onSuccess">
|
||||
</div>
|
||||
break;
|
||||
default:
|
||||
<div class="p-4 bg-yellow-100 dark:bg-yellow-900 rounded-lg">
|
||||
<p class="text-yellow-800 dark:text-yellow-200">
|
||||
Captcha provider not configured correctly.
|
||||
</p>
|
||||
</div>
|
||||
break;
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 text-center text-sm">
|
||||
<div class="font-semibold text-gray-700 dark:text-gray-300 mb-1">Solar Network Anti-Robot</div>
|
||||
<div class="text-gray-600 dark:text-gray-400">
|
||||
Powered by
|
||||
@switch (provider)
|
||||
{
|
||||
case "cloudflare":
|
||||
<a href="https://www.cloudflare.com/turnstile/"
|
||||
class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
|
||||
Cloudflare Turnstile
|
||||
</a>
|
||||
break;
|
||||
case "recaptcha":
|
||||
<a href="https://www.google.com/recaptcha/"
|
||||
class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
|
||||
Google reCaptcha
|
||||
</a>
|
||||
break;
|
||||
default:
|
||||
<span>Nothing</span>
|
||||
break;
|
||||
}
|
||||
<br/>
|
||||
Hosted by
|
||||
<a href="https://github.com/Solsynth/DysonNetwork"
|
||||
class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
|
||||
DysonNetwork.Sphere
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
24
DysonNetwork.Sphere/Pages/Index.cshtml
Normal file
24
DysonNetwork.Sphere/Pages/Index.cshtml
Normal file
@ -0,0 +1,24 @@
|
||||
@page
|
||||
@model IndexModel
|
||||
@{
|
||||
ViewData["Title"] = "The Solar Network";
|
||||
}
|
||||
|
||||
<div class="container-default h-full text-center flex flex-col justify-center items-center">
|
||||
<div class="mx-auto max-w-2xl">
|
||||
<h1 class="text-4xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-6xl">
|
||||
Solar Network
|
||||
</h1>
|
||||
<p class="mt-6 text-lg leading-8 text-gray-600 dark:text-gray-300">
|
||||
This Solar Network instance is up and running.
|
||||
</p>
|
||||
<div class="mt-10 flex items-center justify-center gap-x-6">
|
||||
<a href="https://sn.solsynth.dev" target="_blank" class="btn-primary">
|
||||
Get started
|
||||
</a>
|
||||
<a href="https://kb.solsynth.dev" target="_blank" class="text-sm font-semibold leading-6 text-gray-900 dark:text-gray-100 hover:text-gray-700 dark:hover:text-gray-300">
|
||||
Learn more <span aria-hidden="true">→</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
11
DysonNetwork.Sphere/Pages/Index.cshtml.cs
Normal file
11
DysonNetwork.Sphere/Pages/Index.cshtml.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace DysonNetwork.Sphere.Pages;
|
||||
|
||||
public class IndexModel : PageModel
|
||||
{
|
||||
public void OnGet()
|
||||
{
|
||||
// Add any page initialization logic here
|
||||
}
|
||||
}
|
36
DysonNetwork.Sphere/Pages/Shared/_Layout.cshtml
Normal file
36
DysonNetwork.Sphere/Pages/Shared/_Layout.cshtml
Normal file
@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="h-full">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>@ViewData["Title"]</title>
|
||||
<link rel="stylesheet" href="~/css/styles.css" asp-append-version="true"/>
|
||||
</head>
|
||||
<body class="h-[calc(100dvh-118px)] mt-[64px] bg-white dark:bg-gray-900">
|
||||
<header class="bg-white dark:bg-gray-800 shadow-sm fixed left-0 right-0 top-0 z-50">
|
||||
<nav class="container-default">
|
||||
<div class="flex justify-between h-16 items-center">
|
||||
<div class="flex">
|
||||
<a href="/" class="text-xl font-bold text-gray-900 dark:text-white">Solar Network</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
@* The header 64px + The footer 56px = 118px *@
|
||||
<main class="h-full">
|
||||
@RenderBody()
|
||||
</main>
|
||||
|
||||
<footer class="bg-white dark:bg-gray-800 fixed bottom-0 left-0 right-0 shadow-[0_-1px_3px_0_rgba(0,0,0,0.1)]">
|
||||
<div class="container-default" style="padding: 1rem 0;">
|
||||
<p class="text-center text-gray-500 dark:text-gray-400">
|
||||
© @DateTime.Now.Year Solsynth LLC. All
|
||||
rights reserved.
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@await RenderSectionAsync("Scripts", required: false)
|
||||
</body>
|
||||
</html>
|
@ -1,92 +1,76 @@
|
||||
@page "/spells/{spellWord}"
|
||||
@using DysonNetwork.Sphere.Account
|
||||
@model DysonNetwork.Sphere.Pages.Spell.MagicSpellPage
|
||||
|
||||
@{
|
||||
Layout = null;
|
||||
|
||||
var spell = ViewData["Spell"] as MagicSpell;
|
||||
ViewData["Title"] = "Magic Spell";
|
||||
}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Solar Network Magic Spell</title>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Roboto+Mono&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<style>
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
background-color: #2d2d2d;
|
||||
font-family: "Roboto Mono", monospace;
|
||||
color: #c9d1d9;
|
||||
}
|
||||
<div class="h-full flex items-center justify-center">
|
||||
<div class="max-w-lg w-full mx-auto p-6">
|
||||
<div class="text-center">
|
||||
<h1 class="text-3xl font-bold text-gray-900 dark:text-white mb-4">Magic Spell</h1>
|
||||
|
||||
.parent {
|
||||
padding: 20px;
|
||||
max-width: 480px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
@if (Model.IsSuccess)
|
||||
{
|
||||
<div class="p-4 bg-green-100 dark:bg-green-900 rounded-lg mb-6">
|
||||
<p class="text-green-800 dark:text-green-200">The spell was applied successfully!</p>
|
||||
<p class="text-green-800 dark:text-green-200 opacity-80">Now you can close this page.</p>
|
||||
</div>
|
||||
}
|
||||
else if (Model.CurrentSpell == null)
|
||||
{
|
||||
<div class="p-4 bg-yellow-100 dark:bg-yellow-900 rounded-lg">
|
||||
<p class="text-yellow-800 dark:text-yellow-200">The spell was expired or does not exist.</p>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="px-4 py-12 bg-white dark:bg-gray-800 text-gray-900 dark:text-white shadow-lg rounded-lg mb-6">
|
||||
<div class="mb-2">
|
||||
<p>
|
||||
<span class="font-medium">The spell is for </span>
|
||||
<span class="font-bold">@System.Text.RegularExpressions.Regex.Replace(Model.CurrentSpell!.Type.ToString(), "([a-z])([A-Z])", "$1 $2")</span>
|
||||
</p>
|
||||
<p><span class="font-medium">for @@</span>@Model.CurrentSpell.Account?.Name</p>
|
||||
</div>
|
||||
<div class="text-sm opacity-80">
|
||||
@if (Model.CurrentSpell.ExpiresAt.HasValue)
|
||||
{
|
||||
<p>Available until @Model.CurrentSpell.ExpiresAt.Value.ToDateTimeUtc().ToString("g")</p>
|
||||
}
|
||||
@if (Model.CurrentSpell.AffectedAt.HasValue)
|
||||
{
|
||||
<p>Available after @Model.CurrentSpell.AffectedAt.Value.ToDateTimeUtc().ToString("g")</p>
|
||||
}
|
||||
</div>
|
||||
<p class="text-sm opacity-80">Would you like to apply this spell?</p>
|
||||
</div>
|
||||
|
||||
h2 {
|
||||
font-size: 18px;
|
||||
font-weight: 300;
|
||||
color: #ffffff;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
<form method="post" class="mt-4">
|
||||
<input type="hidden" asp-for="CurrentSpell!.Id"/>
|
||||
<button type="submit"
|
||||
class="px-6 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors
|
||||
transform hover:scale-105 focus:outline-none focus:ring-2 focus:ring-blue-400">
|
||||
Apply
|
||||
</button>
|
||||
</form>
|
||||
}
|
||||
</div>
|
||||
|
||||
.footer {
|
||||
margin-top: 20px;
|
||||
font-size: 11px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.footer-product {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="parent">
|
||||
<div class="container">
|
||||
<h1>Magic Spell</h1>
|
||||
@if (spell == null)
|
||||
{
|
||||
<div>
|
||||
<p style="color: yellow;">The spell was expired or not exists.</p>
|
||||
<div class="mt-8 text-center text-sm">
|
||||
<div class="font-semibold text-gray-700 dark:text-gray-300 mb-1">Solar Network</div>
|
||||
<div class="text-gray-600 dark:text-gray-400">
|
||||
<a href="https://solsynth.dev" class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
|
||||
Solsynth LLC
|
||||
</a>
|
||||
© @DateTime.Now.Year
|
||||
<br/>
|
||||
Powered by
|
||||
<a href="https://github.com/Solsynth/DysonNetwork"
|
||||
class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
|
||||
DysonNetwork.Sphere
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div>
|
||||
<p style="color: green;">The spell was applied successfully!</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="footer-product">Solar Network</div>
|
||||
<a
|
||||
href="https://solsynth.dev"
|
||||
style="color: #c9d1d9; text-decoration: none"
|
||||
>Solsynth LLC</a>
|
||||
© @DateTime.Now.Year<br/>
|
||||
Powered by
|
||||
<a
|
||||
href="https://github.com/Solsynth/DysonNetwork"
|
||||
style="color: #c9d1d9"
|
||||
>DysonNetwork.Sphere</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</div>
|
@ -8,23 +8,42 @@ namespace DysonNetwork.Sphere.Pages.Spell;
|
||||
|
||||
public class MagicSpellPage(AppDatabase db, MagicSpellService spells) : PageModel
|
||||
{
|
||||
[BindProperty]
|
||||
public MagicSpell? CurrentSpell { get; set; }
|
||||
|
||||
public bool IsSuccess { get; set; }
|
||||
|
||||
public async Task<IActionResult> OnGetAsync(string spellWord)
|
||||
{
|
||||
spellWord = Uri.UnescapeDataString(spellWord);
|
||||
var now = SystemClock.Instance.GetCurrentInstant();
|
||||
var spell = await db.MagicSpells
|
||||
CurrentSpell = await db.MagicSpells
|
||||
.Where(e => e.Spell == spellWord)
|
||||
.Where(e => e.ExpiresAt == null || now < e.ExpiresAt)
|
||||
.Where(e => e.AffectedAt == null || now >= e.AffectedAt)
|
||||
.Include(e => e.Account)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
ViewData["Spell"] = spell;
|
||||
|
||||
if (spell is not null)
|
||||
{
|
||||
await spells.ApplyMagicSpell(spell);
|
||||
}
|
||||
|
||||
return Page();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPostAsync()
|
||||
{
|
||||
if (CurrentSpell?.Id == null)
|
||||
return Page();
|
||||
|
||||
var now = SystemClock.Instance.GetCurrentInstant();
|
||||
var spell = await db.MagicSpells
|
||||
.Where(e => e.Id == CurrentSpell.Id)
|
||||
.Where(e => e.ExpiresAt == null || now < e.ExpiresAt)
|
||||
.Where(e => e.AffectedAt == null || now >= e.AffectedAt)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (spell == null)
|
||||
return Page();
|
||||
|
||||
await spells.ApplyMagicSpell(spell);
|
||||
IsSuccess = true;
|
||||
return Page();
|
||||
}
|
||||
}
|
3
DysonNetwork.Sphere/Pages/_ViewImports.cshtml
Normal file
3
DysonNetwork.Sphere/Pages/_ViewImports.cshtml
Normal file
@ -0,0 +1,3 @@
|
||||
@using DysonNetwork.Sphere
|
||||
@namespace DysonNetwork.Sphere.Pages
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
3
DysonNetwork.Sphere/Pages/_ViewStart.cshtml
Normal file
3
DysonNetwork.Sphere/Pages/_ViewStart.cshtml
Normal file
@ -0,0 +1,3 @@
|
||||
@{
|
||||
Layout = "_Layout";
|
||||
}
|
Reference in New Issue
Block a user