mirror of
https://github.com/1f349/lavender.git
synced 2025-01-21 06:06:30 +00:00
Convert other pages to astro
This commit is contained in:
parent
713d53b7b2
commit
bb162efb8c
@ -3,11 +3,14 @@ import {defineConfig} from 'astro/config';
|
||||
|
||||
import tailwind from '@astrojs/tailwind';
|
||||
|
||||
import svelte from '@astrojs/svelte';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
integrations: [
|
||||
tailwind({
|
||||
nesting: true,
|
||||
})
|
||||
]
|
||||
integrations: [tailwind({
|
||||
nesting: true,
|
||||
}), svelte()],
|
||||
build: {
|
||||
format: 'file',
|
||||
},
|
||||
});
|
||||
|
@ -13,9 +13,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/check": "^0.9.4",
|
||||
"@astrojs/svelte": "^5.7.3",
|
||||
"@astrojs/tailwind": "^5.1.3",
|
||||
"astro": "^4.16.16",
|
||||
"svelte": "^4.2.19",
|
||||
"tailwindcss": "^3.4.16",
|
||||
"typescript": "^5.7.2"
|
||||
"typescript": "^5.7.3"
|
||||
}
|
||||
}
|
||||
|
31
web/src/components/CopyTextComponent.svelte
Normal file
31
web/src/components/CopyTextComponent.svelte
Normal file
@ -0,0 +1,31 @@
|
||||
<script lang="ts">
|
||||
import {onMount} from "svelte";
|
||||
|
||||
let textCopyElement: HTMLSpanElement;
|
||||
|
||||
export let text: string;
|
||||
|
||||
onMount(() => {
|
||||
selectText(textCopyElement);
|
||||
});
|
||||
|
||||
// Thanks again: https://stackoverflow.com/a/987376
|
||||
function selectText(node: HTMLElement) {
|
||||
|
||||
if ((document.body as any).createTextRange) {
|
||||
const range = (document.body as any).createTextRange();
|
||||
range.moveToElementText(node);
|
||||
range.select();
|
||||
} else if (window.getSelection) {
|
||||
const selection = window.getSelection();
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(node as any);
|
||||
(selection as any).removeAllRanges();
|
||||
(selection as any).addRange(range);
|
||||
} else {
|
||||
console.warn("Could not select text in node: Unsupported browser.");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<span bind:this={textCopyElement}>{text}</span>
|
@ -2,6 +2,10 @@
|
||||
import Header from "../components/Header.astro";
|
||||
|
||||
const {title} = Astro.props;
|
||||
|
||||
function renderTitle(title: string): string {
|
||||
return (title ? `${title} | ` : "") + "[[ .ServiceName ]]";
|
||||
}
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
@ -11,7 +15,7 @@ const {title} = Astro.props;
|
||||
<meta name="viewport" content="width=device-width"/>
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg"/>
|
||||
<meta name="generator" content={Astro.generator}/>
|
||||
<title>[[ renderTitle "{title}" .ServiceName ]]</title>
|
||||
<title>{renderTitle(title)}</title>
|
||||
</head>
|
||||
<body>
|
||||
<Header/>
|
||||
|
19
web/src/pages/edit-otp.astro
Normal file
19
web/src/pages/edit-otp.astro
Normal file
@ -0,0 +1,19 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
---
|
||||
|
||||
<Layout title="Edit OTP">
|
||||
<form method="POST" action="/edit/otp">
|
||||
<input type="hidden" name="secret" value="[[ .OtpSecret ]]"/>
|
||||
<input type="hidden" name="digits" value="[[ .OtpDigits ]]"/>
|
||||
<p>
|
||||
<img src="[[ .OtpQr ]]" style="width:[[ .QrWidth ]]px" alt="OTP QR code not loading"/>
|
||||
</p>
|
||||
<p style="display:none">Raw OTP string: [[ .OtpUrl ]]</p>
|
||||
<div>
|
||||
<label for="field_code">OTP Code:</label>
|
||||
<input type="text" name="code" id="field_code" required autofocus pattern="[0-9]{6,8}" title="6/7/8 digit one time passcode"/>
|
||||
</div>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
</Layout>
|
@ -1,27 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{.ServiceName}}</title>
|
||||
<link rel="stylesheet" href="/theme/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>{{.ServiceName}}</h1>
|
||||
</header>
|
||||
<main>
|
||||
<form method="POST" action="/edit/otp">
|
||||
<input type="hidden" name="secret" value="{{.OtpSecret}}"/>
|
||||
<input type="hidden" name="digits" value="{{.OtpDigits}}"/>
|
||||
<p>
|
||||
<img src="{{.OtpQr}}" style="width:{{.QrWidth}}px" alt="OTP QR code not loading"/>
|
||||
</p>
|
||||
<p style="display:none">Raw OTP string: {{.OtpUrl}}</p>
|
||||
<div>
|
||||
<label for="field_code">OTP Code:</label>
|
||||
<input type="text" name="code" id="field_code" required autofocus pattern="[0-9]{6,8}" title="6/7/8 digit one time passcode"/>
|
||||
</div>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
21
web/src/pages/edit-password.astro
Normal file
21
web/src/pages/edit-password.astro
Normal file
@ -0,0 +1,21 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
---
|
||||
|
||||
<Layout title="Edit Password">
|
||||
<form method="POST" action="/edit/password">
|
||||
<div>
|
||||
<label for="field_current_password">Current Password:</label>
|
||||
<input type="password" name="password" id="field_current_password" autocomplete="password" autofocus required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_new_password">New Password:</label>
|
||||
<input type="password" name="password" id="field_new_password" autocomplete="new_password" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_retype_password">Retype New Password:</label>
|
||||
<input type="password" name="password" id="field_retype_password" autocomplete="confirm_password" required/>
|
||||
</div>
|
||||
<button type="submit">Change Password</button>
|
||||
</form>
|
||||
</Layout>
|
@ -1,29 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{.ServiceName}}</title>
|
||||
<link rel="stylesheet" href="/theme/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>{{.ServiceName}}</h1>
|
||||
</header>
|
||||
<main>
|
||||
<form method="POST" action="/edit/password">
|
||||
<div>
|
||||
<label for="field_password">Current Password:</label>
|
||||
<input type="password" name="password" id="field_password" autocomplete="password" autofocus required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_password">New Password:</label>
|
||||
<input type="password" name="password" id="field_password" autocomplete="new_password" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_password">Retype New Password:</label>
|
||||
<input type="password" name="password" id="field_password" autocomplete="confirm_password" required/>
|
||||
</div>
|
||||
<button type="submit">Change Password</button>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
@ -2,7 +2,7 @@
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
---
|
||||
|
||||
<Layout title="Edit">
|
||||
<Layout title="Edit Profile">
|
||||
<div>Logged in as: [[.User.Name]] ([[.User.Subject]])</div>
|
||||
<div>
|
||||
<form method="POST" action="/edit">
|
||||
@ -22,11 +22,11 @@ import Layout from "../layouts/Layout.astro";
|
||||
<div>
|
||||
<label for="field_pronouns">Pronouns:</label>
|
||||
<select name="pronouns" id="field_pronouns">
|
||||
<option value="they/them" [[ if eq "they/them" .FieldPronoun ]]selected[[ end ]]>They/Them</option>
|
||||
<option value="he/him" [[ if eq "he/him" .FieldPronoun ]]selected[[ end ]]>He/Him</option>
|
||||
<option value="she/her" [[ if eq "she/her" .FieldPronoun ]]selected[[ end ]]>She/Her</option>
|
||||
<option value="it/its" [[ if eq "it/its" .FieldPronoun ]]selected[[ end ]]>It/Its</option>
|
||||
<option value="one/one's" [[ if eq "one/one's" .FieldPronoun ]]selected[[ end ]]>One/One's</option>
|
||||
[[ renderOptionTag "they/them" "They/Them" .FieldPronoun ]]
|
||||
[[ renderOptionTag "he/him" "He/Him" .FieldPronoun ]]
|
||||
[[ renderOptionTag "she/her" "She/Her" .FieldPronoun ]]
|
||||
[[ renderOptionTag "it/its" "It/Its" .FieldPronoun ]]
|
||||
[[ renderOptionTag "one/one's" "One/One's" .FieldPronoun ]]
|
||||
</select>
|
||||
<label>Reset? <input type="checkbox" name="reset_pronouns"></label>
|
||||
</div>
|
||||
|
12
web/src/pages/index-guest.astro
Normal file
12
web/src/pages/index-guest.astro
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<div>Not logged in</div>
|
||||
<div>
|
||||
<form method="GET" action="/login">
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
</Layout>
|
@ -1,19 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{.ServiceName}}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="/assets/style.css">
|
||||
</head>
|
||||
<body>
|
||||
{{template "header.go.html" .}}
|
||||
<main>
|
||||
<div>Not logged in</div>
|
||||
<div>
|
||||
<form method="GET" action="/login">
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
@ -2,7 +2,7 @@
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
---
|
||||
|
||||
<Layout title="Welcome to Astro.">
|
||||
<Layout>
|
||||
<div class="center-box sm:max-w-md">
|
||||
<div>Logged in as: [[ .Auth.UserInfo.name ]] ([[ .Auth.Subject ]])</div>
|
||||
<form method="GET" action="/manage/apps" class="space-y-4 md:space-y-6">
|
||||
@ -13,6 +13,19 @@ import Layout from '../layouts/Layout.astro';
|
||||
<button type="submit" class="btn-green">Manage Users</button>
|
||||
</form>
|
||||
[[ end ]]
|
||||
[[ if .OtpEnabled ]]
|
||||
<form method="POST" action="/edit/otp">
|
||||
<input type="hidden" name="remove" value="1"/>
|
||||
<button type="submit">Remove OTP</button>
|
||||
</form>
|
||||
[[ else ]]
|
||||
<form method="POST" action="/edit/otp">
|
||||
<label><input type="radio" name="digits" value="6"/> 6 digits</label>
|
||||
<label><input type="radio" name="digits" value="7"/> 7 digits</label>
|
||||
<label><input type="radio" name="digits" value="8"/> 8 digits</label>
|
||||
<button type="submit">Change OTP</button>
|
||||
</form>
|
||||
[[ end ]]
|
||||
<form method="POST" action="/logout">
|
||||
<input type="hidden" name="nonce" value="[[ .Nonce ]]">
|
||||
<button type="submit" class="btn-red">Log out</button>
|
||||
|
@ -1,49 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{.ServiceName}}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<link rel="stylesheet" href="/assets/style.css">
|
||||
</head>
|
||||
<body>
|
||||
{{template "header.go.html" .}}
|
||||
<main>
|
||||
<div>Logged in as: {{.Auth.UserInfo.name}} ({{.Auth.Subject}})</div>
|
||||
<div>
|
||||
<form method="GET" action="/manage/apps">
|
||||
<button type="submit">Manage Applications</button>
|
||||
</form>
|
||||
</div>
|
||||
{{if .IsAdmin}}
|
||||
<div>
|
||||
<form method="GET" action="/manage/users">
|
||||
<button type="submit">Manage Users</button>
|
||||
</form>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .OtpEnabled}}
|
||||
<div>
|
||||
<form method="POST" action="/edit/otp">
|
||||
<input type="hidden" name="remove" value="1"/>
|
||||
<button type="submit">Remove OTP</button>
|
||||
</form>
|
||||
</div>
|
||||
{{else}}
|
||||
<div>
|
||||
<form method="POST" action="/edit/otp">
|
||||
<label><input type="radio" name="digits" value="6"/> 6 digits</label>
|
||||
<label><input type="radio" name="digits" value="7"/> 7 digits</label>
|
||||
<label><input type="radio" name="digits" value="8"/> 8 digits</label>
|
||||
<button type="submit">Change OTP</button>
|
||||
</form>
|
||||
</div>
|
||||
{{end}}
|
||||
<div>
|
||||
<form method="POST" action="/logout">
|
||||
<input type="hidden" name="nonce" value="{{.Nonce}}">
|
||||
<button type="submit">Log Out</button>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
19
web/src/pages/login-memory.astro
Normal file
19
web/src/pages/login-memory.astro
Normal file
@ -0,0 +1,19 @@
|
||||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<div>Log in as: <span>[[ .LoginName ]]</span></div>
|
||||
<div>
|
||||
<form method="POST" action="/login">
|
||||
<button type="submit" name="not-you" value="1">Not You?</button>
|
||||
</form>
|
||||
</div>
|
||||
<div>
|
||||
<form method="POST" action="/login">
|
||||
<input type="hidden" name="redirect" value="[[ .Redirect ]]"/>
|
||||
<input type="hidden" name="loginname" value="[[ .LoginName ]]"/>
|
||||
<button type="submit">Continue</button>
|
||||
</form>
|
||||
</div>
|
||||
</Layout>
|
@ -1,26 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{.ServiceName}}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="/assets/style.css">
|
||||
</head>
|
||||
<body>
|
||||
{{template "header.go.html" .}}
|
||||
<main>
|
||||
<div>Log in as: <span>{{.LoginName}}</span></div>
|
||||
<div>
|
||||
<form method="POST" action="/login">
|
||||
<button type="submit" name="not-you" value="1">Not You?</button>
|
||||
</form>
|
||||
</div>
|
||||
<div>
|
||||
<form method="POST" action="/login">
|
||||
<input type="hidden" name="redirect" value="{{.Redirect}}"/>
|
||||
<input type="hidden" name="loginname" value="{{.LoginName}}"/>
|
||||
<button type="submit">Continue</button>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
@ -11,7 +11,7 @@ import Layout from "../layouts/Layout.astro";
|
||||
[[ .AuthTemplate ]]
|
||||
[[ if gt (len .AuthButtons) 0 ]]
|
||||
<div class="auth-buttons">
|
||||
[[ $authButton := range .AuthButtons ]]
|
||||
[[ range $authButton := .AuthButtons ]]
|
||||
[[ . ]]
|
||||
[[ end ]]
|
||||
</div>
|
||||
|
41
web/src/pages/manage-apps-create.astro
Normal file
41
web/src/pages/manage-apps-create.astro
Normal file
@ -0,0 +1,41 @@
|
||||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<form method="GET" action="/">
|
||||
<button type="submit">Home</button>
|
||||
</form>
|
||||
|
||||
<h2>Create Client Application</h2>
|
||||
<form method="POST" action="/manage/apps">
|
||||
<input type="hidden" name="action" value="create"/>
|
||||
<input type="hidden" name="offset" value="[[ .Offset ]]"/>
|
||||
<div>
|
||||
<label for="field_name">Name:</label>
|
||||
<input type="text" name="name" id="field_name" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_domain">Domain:</label>
|
||||
<input type="text" name="domain" id="field_domain" required/>
|
||||
</div>
|
||||
[[ if .IsAdmin ]]
|
||||
<div>
|
||||
<label for="field_perms">Perms:</label>
|
||||
<input type="text" name="perms" id="field_perms"/>
|
||||
</div>
|
||||
[[ end ]]
|
||||
<div>
|
||||
<label for="field_public">Public: <input type="checkbox" name="public" id="field_public"/></label>
|
||||
</div>
|
||||
[[ if .IsAdmin ]]
|
||||
<div>
|
||||
<label for="field_sso">SSO: <input type="checkbox" name="sso" id="field_sso"/></label>
|
||||
</div>
|
||||
[[ end ]]
|
||||
<div>
|
||||
<label for="field_active">Active: <input type="checkbox" name="active" id="field_active" checked/></label>
|
||||
</div>
|
||||
<button type="submit">Create</button>
|
||||
</form>
|
||||
</Layout>
|
@ -1,72 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{.ServiceName}}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<link rel="stylesheet" href="/assets/style.css">
|
||||
<script>
|
||||
window.addEventListener("load", function () {
|
||||
selectText("app-secret");
|
||||
});
|
||||
|
||||
// Thanks again: https://stackoverflow.com/a/987376
|
||||
function selectText(nodeId) {
|
||||
const node = document.getElementById(nodeId);
|
||||
|
||||
if (document.body.createTextRange) {
|
||||
const range = document.body.createTextRange();
|
||||
range.moveToElementText(node);
|
||||
range.select();
|
||||
} else if (window.getSelection) {
|
||||
const selection = window.getSelection();
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(node);
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
} else {
|
||||
console.warn("Could not select text in node: Unsupported browser.");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
{{template "header.go.html" .}}
|
||||
<main>
|
||||
<form method="GET" action="/">
|
||||
<button type="submit">Home</button>
|
||||
</form>
|
||||
|
||||
<h2>Create Client Application</h2>
|
||||
<form method="POST" action="/manage/apps">
|
||||
<input type="hidden" name="action" value="create"/>
|
||||
<input type="hidden" name="offset" value="{{.Offset}}"/>
|
||||
<div>
|
||||
<label for="field_name">Name:</label>
|
||||
<input type="text" name="name" id="field_name" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_domain">Domain:</label>
|
||||
<input type="text" name="domain" id="field_domain" required/>
|
||||
</div>
|
||||
{{if .IsAdmin}}
|
||||
<div>
|
||||
<label for="field_perms">Perms:</label>
|
||||
<input type="text" name="perms" id="field_perms"/>
|
||||
</div>
|
||||
{{end}}
|
||||
<div>
|
||||
<label for="field_public">Public: <input type="checkbox" name="public" id="field_public"/></label>
|
||||
</div>
|
||||
{{if .IsAdmin}}
|
||||
<div>
|
||||
<label for="field_sso">SSO: <input type="checkbox" name="sso" id="field_sso"/></label>
|
||||
</div>
|
||||
{{end}}
|
||||
<div>
|
||||
<label for="field_active">Active: <input type="checkbox" name="active" id="field_active" checked/></label>
|
||||
</div>
|
||||
<button type="submit">Create</button>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
49
web/src/pages/manage-apps-edit.astro
Normal file
49
web/src/pages/manage-apps-edit.astro
Normal file
@ -0,0 +1,49 @@
|
||||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<form method="GET" action="/">
|
||||
<button type="submit">Home</button>
|
||||
</form>
|
||||
|
||||
<h2>Edit Client Application</h2>
|
||||
<form method="POST" action="/manage/apps">
|
||||
<input type="hidden" name="action" value="edit"/>
|
||||
<input type="hidden" name="offset" value="[[ .Offset ]]"/>
|
||||
<input type="hidden" name="subject" value="[[ .EditApp.Subject ]]"/>
|
||||
<div>
|
||||
<label>ID: [[ .EditApp.Subject ]]</label>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_name">Name:</label>
|
||||
<input type="text" name="name" id="field_name" value="[[ .EditApp.Name ]]" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_domain">Domain:</label>
|
||||
<input type="text" name="domain" id="field_domain" value="[[ .EditApp.Domain ]]" required/>
|
||||
</div>
|
||||
[[ if .IsAdmin ]]
|
||||
<div>
|
||||
<label for="field_perms">Perms:</label>
|
||||
<input type="text" name="perms" id="field_perms" value="[[ .EditApp.Perms ]]" size="100"/>
|
||||
</div>
|
||||
[[ end ]]
|
||||
<div>
|
||||
<label for="field_public">Public: [[ renderCheckboxTag "public" "field_public" .EditApp.Public ]]</label>
|
||||
</div>
|
||||
[[ if .IsAdmin ]]
|
||||
<div>
|
||||
<label for="field_sso">SSO: [[ renderCheckboxTag "sso" "field_sso" .EditApp.Sso ]]</label>
|
||||
</div>
|
||||
[[ end ]]
|
||||
<div>
|
||||
<label for="field_active">Active: [[ renderCheckboxTag "active" "field_active" .EditApp.Active ]]</label>
|
||||
</div>
|
||||
<button type="submit">Edit</button>
|
||||
</form>
|
||||
<form method="GET" action="/manage/apps">
|
||||
<input type="hidden" name="offset" value="[[ .Offset ]]"/>
|
||||
<button type="submit">Cancel</button>
|
||||
</form>
|
||||
</Layout>
|
@ -1,80 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{.ServiceName}}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<link rel="stylesheet" href="/assets/style.css">
|
||||
<script>
|
||||
window.addEventListener("load", function () {
|
||||
selectText("app-secret");
|
||||
});
|
||||
|
||||
// Thanks again: https://stackoverflow.com/a/987376
|
||||
function selectText(nodeId) {
|
||||
const node = document.getElementById(nodeId);
|
||||
|
||||
if (document.body.createTextRange) {
|
||||
const range = document.body.createTextRange();
|
||||
range.moveToElementText(node);
|
||||
range.select();
|
||||
} else if (window.getSelection) {
|
||||
const selection = window.getSelection();
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(node);
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
} else {
|
||||
console.warn("Could not select text in node: Unsupported browser.");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
{{template "header.go.html" .}}
|
||||
<main>
|
||||
<form method="GET" action="/">
|
||||
<button type="submit">Home</button>
|
||||
</form>
|
||||
|
||||
<h2>Edit Client Application</h2>
|
||||
<form method="POST" action="/manage/apps">
|
||||
<input type="hidden" name="action" value="edit"/>
|
||||
<input type="hidden" name="offset" value="{{.Offset}}"/>
|
||||
<input type="hidden" name="subject" value="{{.EditApp.Subject}}"/>
|
||||
<div>
|
||||
<label>ID: {{.EditApp.Subject}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_name">Name:</label>
|
||||
<input type="text" name="name" id="field_name" value="{{.EditApp.Name}}" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_domain">Domain:</label>
|
||||
<input type="text" name="domain" id="field_domain" value="{{.EditApp.Domain}}" required/>
|
||||
</div>
|
||||
{{if .IsAdmin}}
|
||||
<div>
|
||||
<label for="field_perms">Perms:</label>
|
||||
<input type="text" name="perms" id="field_perms" value="{{.EditApp.Perms}}" size="100"/>
|
||||
</div>
|
||||
{{end}}
|
||||
<div>
|
||||
<label for="field_public">Public: <input type="checkbox" name="public" id="field_public" {{if .EditApp.Public}}checked{{end}}/></label>
|
||||
</div>
|
||||
{{if .IsAdmin}}
|
||||
<div>
|
||||
<label for="field_sso">SSO: <input type="checkbox" name="sso" id="field_sso" {{if .EditApp.Sso}}checked{{end}}/></label>
|
||||
</div>
|
||||
{{end}}
|
||||
<div>
|
||||
<label for="field_active">Active: <input type="checkbox" name="active" id="field_active" {{if .EditApp.Active}}checked{{end}}/></label>
|
||||
</div>
|
||||
<button type="submit">Edit</button>
|
||||
</form>
|
||||
<form method="GET" action="/manage/apps">
|
||||
<input type="hidden" name="offset" value="{{.Offset}}"/>
|
||||
<button type="submit">Cancel</button>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
66
web/src/pages/manage-apps.astro
Normal file
66
web/src/pages/manage-apps.astro
Normal file
@ -0,0 +1,66 @@
|
||||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import CopyTextComponent from '../components/CopyTextComponent.svelte';
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<form method="GET" action="/">
|
||||
<button type="submit">Home</button>
|
||||
</form>
|
||||
|
||||
[[ if .NewAppSecret ]]
|
||||
<div>New application secret:
|
||||
<CopyTextComponent client:only="svelte" text="[[ .NewAppSecret ]]"/>
|
||||
for [[ .NewAppName ]]
|
||||
</div>
|
||||
[[ end ]]
|
||||
|
||||
<h2>Manage Client Applications</h2>
|
||||
<form method="GET" action="/manage/apps/create">
|
||||
<button type="submit">New Client Application</button>
|
||||
</form>
|
||||
[[ if eq (len .Apps) 0 ]]
|
||||
<div>No client applications found</div>
|
||||
[[ else ]]
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Domain</th>
|
||||
<th>Perms</th>
|
||||
<th>SSO</th>
|
||||
<th>Active</th>
|
||||
<th>Owner</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
[[ range .Apps ]]
|
||||
<tr>
|
||||
<td>[[ .Subject ]]</td>
|
||||
<td>[[ .Name ]]</td>
|
||||
<td>[[ .Domain ]]</td>
|
||||
<td>[[ .Perms ]]</td>
|
||||
<td>[[ .Sso ]]</td>
|
||||
<td>[[ .Active ]]</td>
|
||||
<td>[[ .Owner ]]</td>
|
||||
<td>
|
||||
<form method="GET" action="/manage/apps">
|
||||
<input type="hidden" name="offset" value="[[ $.Offset ]]"/>
|
||||
<input type="hidden" name="edit" value="[[ .Subject ]]"/>
|
||||
<button type="submit">Edit</button>
|
||||
</form>
|
||||
<form method="POST" action="/manage/apps?offset=[[ $.Offset ]]">
|
||||
<input type="hidden" name="action" value="secret"/>
|
||||
<input type="hidden" name="offset" value="[[ $.Offset ]]"/>
|
||||
<input type="hidden" name="subject" value="[[ .Subject ]]"/>
|
||||
<button type="submit">Reset Secret</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
[[ end ]]
|
||||
</tbody>
|
||||
</table>
|
||||
[[ end ]]
|
||||
</Layout>
|
@ -1,93 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{.ServiceName}}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<link rel="stylesheet" href="/assets/style.css">
|
||||
<script>
|
||||
window.addEventListener("load", function () {
|
||||
selectText("app-secret");
|
||||
});
|
||||
|
||||
// Thanks again: https://stackoverflow.com/a/987376
|
||||
function selectText(nodeId) {
|
||||
const node = document.getElementById(nodeId);
|
||||
|
||||
if (document.body.createTextRange) {
|
||||
const range = document.body.createTextRange();
|
||||
range.moveToElementText(node);
|
||||
range.select();
|
||||
} else if (window.getSelection) {
|
||||
const selection = window.getSelection();
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(node);
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
} else {
|
||||
console.warn("Could not select text in node: Unsupported browser.");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
{{template "header.go.html" .}}
|
||||
<main>
|
||||
<form method="GET" action="/">
|
||||
<button type="submit">Home</button>
|
||||
</form>
|
||||
|
||||
{{if .NewAppSecret}}
|
||||
<div>New application secret: <span id="app-secret">{{.NewAppSecret}}</span> for {{.NewAppName}}</div>
|
||||
{{end}}
|
||||
|
||||
<h2>Manage Client Applications</h2>
|
||||
<form method="GET" action="/manage/apps/create">
|
||||
<button type="submit">New Client Application</button>
|
||||
</form>
|
||||
{{if eq (len .Apps) 0}}
|
||||
<div>No client applications found</div>
|
||||
{{else}}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Domain</th>
|
||||
<th>Perms</th>
|
||||
<th>SSO</th>
|
||||
<th>Active</th>
|
||||
<th>Owner</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .Apps}}
|
||||
<tr>
|
||||
<td>{{.Subject}}</td>
|
||||
<td>{{.Name}}</td>
|
||||
<td>{{.Domain}}</td>
|
||||
<td>{{.Perms}}</td>
|
||||
<td>{{.Sso}}</td>
|
||||
<td>{{.Active}}</td>
|
||||
<td>{{.Owner}}</td>
|
||||
<td>
|
||||
<form method="GET" action="/manage/apps">
|
||||
<input type="hidden" name="offset" value="{{$.Offset}}"/>
|
||||
<input type="hidden" name="edit" value="{{.Subject}}"/>
|
||||
<button type="submit">Edit</button>
|
||||
</form>
|
||||
<form method="POST" action="/manage/apps?offset={{$.Offset}}">
|
||||
<input type="hidden" name="action" value="secret"/>
|
||||
<input type="hidden" name="offset" value="{{$.Offset}}"/>
|
||||
<input type="hidden" name="subject" value="{{.Subject}}"/>
|
||||
<button type="submit">Reset Secret</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{end}}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
38
web/src/pages/manage-users-create.astro
Normal file
38
web/src/pages/manage-users-create.astro
Normal file
@ -0,0 +1,38 @@
|
||||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<form method="GET" action="/">
|
||||
<button type="submit">Home</button>
|
||||
</form>
|
||||
|
||||
<h2>Create User</h2>
|
||||
<form method="POST" action="/manage/users">
|
||||
<input type="hidden" name="action" value="create"/>
|
||||
<input type="hidden" name="offset" value="[[ .Offset ]]"/>
|
||||
<div>
|
||||
<label for="field_name">Name:</label>
|
||||
<input type="text" name="name" id="field_name" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_username">Username:</label>
|
||||
<input type="text" name="username" id="field_username" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_email">Email:</label>
|
||||
<p>Using an `@[[ .Namespace ]]` email address will automatically verify as it is owned by this login
|
||||
service.</p>
|
||||
<input type="text" name="email" id="field_email" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_role">Roles:</label>
|
||||
<input type="text" name="roles" id="field_roles" value="[[ .EditUser.Roles ]]" size="100"/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_active">Active: <input type="checkbox" name="active" id="field_active"
|
||||
checked/></label>
|
||||
</div>
|
||||
<button type="submit">Create</button>
|
||||
</form>
|
||||
</Layout>
|
@ -1,46 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{.ServiceName}}</title>
|
||||
<link rel="stylesheet" href="/theme/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>{{.ServiceName}}</h1>
|
||||
</header>
|
||||
<main>
|
||||
<form method="GET" action="/">
|
||||
<button type="submit">Home</button>
|
||||
</form>
|
||||
|
||||
<h2>Create User</h2>
|
||||
<form method="POST" action="/manage/users">
|
||||
<input type="hidden" name="action" value="create"/>
|
||||
<input type="hidden" name="offset" value="{{.Offset}}"/>
|
||||
<div>
|
||||
<label for="field_name">Name:</label>
|
||||
<input type="text" name="name" id="field_name" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_username">Username:</label>
|
||||
<input type="text" name="username" id="field_username" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_email">Email:</label>
|
||||
<p>Using an `@{{.Namespace}}` email address will automatically verify as it is owned by this login
|
||||
service.</p>
|
||||
<input type="text" name="email" id="field_email" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_role">Roles:</label>
|
||||
<input type="text" name="roles" id="field_roles" value="{{.EditUser.Roles}}" size="100"/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_active">Active: <input type="checkbox" name="active" id="field_active"
|
||||
checked/></label>
|
||||
</div>
|
||||
<button type="submit">Create</button>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
31
web/src/pages/manage-users-edit.astro
Normal file
31
web/src/pages/manage-users-edit.astro
Normal file
@ -0,0 +1,31 @@
|
||||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<form method="GET" action="/">
|
||||
<button type="submit">Home</button>
|
||||
</form>
|
||||
|
||||
<h2>Edit User</h2>
|
||||
<form method="POST" action="/manage/users">
|
||||
<input type="hidden" name="action" value="edit"/>
|
||||
<input type="hidden" name="offset" value="[[ .Offset ]]"/>
|
||||
<div>
|
||||
<label for="field_subject">Subject:</label>
|
||||
<input type="text" name="subject" id="field_subject" value="[[ .EditUser.Subject ]]" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_roles">Roles:</label>
|
||||
<input type="text" name="roles" id="field_roles" value="[[ .EditUser.Roles ]]" size="100"/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_active">Active: <input type="checkbox" name="active" id="field_active" checked/></label>
|
||||
</div>
|
||||
<button type="submit">Edit</button>
|
||||
</form>
|
||||
<form method="GET" action="/manage/users">
|
||||
<input type="hidden" name="offset" value="[[ .Offset ]]"/>
|
||||
<button type="submit">Cancel</button>
|
||||
</form>
|
||||
</Layout>
|
@ -1,38 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{.ServiceName}}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<link rel="stylesheet" href="/assets/style.css">
|
||||
</head>
|
||||
<body>
|
||||
{{template "header.go.html" .}}
|
||||
<main>
|
||||
<form method="GET" action="/">
|
||||
<button type="submit">Home</button>
|
||||
</form>
|
||||
|
||||
<h2>Edit User</h2>
|
||||
<form method="POST" action="/manage/users">
|
||||
<input type="hidden" name="action" value="edit"/>
|
||||
<input type="hidden" name="offset" value="{{.Offset}}"/>
|
||||
<div>
|
||||
<label for="field_subject">Subject:</label>
|
||||
<input type="text" name="subject" id="field_subject" value="{{.EditUser.Subject}}" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_roles">Roles:</label>
|
||||
<input type="text" name="roles" id="field_roles" value="{{.EditUser.Roles}}" size="100"/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_active">Active: <input type="checkbox" name="active" id="field_active" checked/></label>
|
||||
</div>
|
||||
<button type="submit">Edit</button>
|
||||
</form>
|
||||
<form method="GET" action="/manage/users">
|
||||
<input type="hidden" name="offset" value="{{.Offset}}"/>
|
||||
<button type="submit">Cancel</button>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
64
web/src/pages/manage-users.astro
Normal file
64
web/src/pages/manage-users.astro
Normal file
@ -0,0 +1,64 @@
|
||||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<form method="GET" action="/">
|
||||
<button type="submit">Home</button>
|
||||
</form>
|
||||
|
||||
<h2>Manage Users</h2>
|
||||
[[ if eq (len.Users) 0 ]]
|
||||
<div>No users found, this is definitely a bug.</div>
|
||||
[[ else ]]
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Subject</th>
|
||||
<th>Email</th>
|
||||
<th>Email Verified</th>
|
||||
<th>Roles</th>
|
||||
<th>Last Updated</th>
|
||||
<th>Active</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
[[ range .Users ]]
|
||||
<tr>
|
||||
<td>[[ .Subject ]]</td>
|
||||
<th>
|
||||
[[ if $.EmailShow ]]
|
||||
<span>[[ .Email ]]</span>
|
||||
[[ else ]]
|
||||
<span>[[ emailHide .Email ]]</span>
|
||||
[[ end ]]
|
||||
</th>
|
||||
<th>[[ .EmailVerified ]]</th>
|
||||
<th>[[ .Roles ]]</th>
|
||||
<th>[[ .UpdatedAt ]]</th>
|
||||
<td>[[ .Active ]]</td>
|
||||
<td>
|
||||
<form method="GET" action="/manage/users">
|
||||
<input type="hidden" name="offset" value="[[ $.Offset ]]"/>
|
||||
<input type="hidden" name="edit" value="[[ .Subject ]]"/>
|
||||
<button type="submit">Edit</button>
|
||||
</form>
|
||||
<form method="POST" action="/reset-password">
|
||||
<input type="hidden" name="email" value="[[ .Email ]]"/>
|
||||
<button type="submit">Send Reset Password Email</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
[[ end ]]
|
||||
</tbody>
|
||||
</table>
|
||||
<form method="GET" action="/manage/users">
|
||||
<input type="hidden" name="offset" value="[[ .Offset ]]"/>
|
||||
[[ if not.EmailShow ]]
|
||||
<input type="hidden" name="show-email"/>
|
||||
[[ end ]]
|
||||
<button type="submit">[[ if .EmailShow ]]Hide Email Addresses[[ else ]]Show email addresses[[ end ]]</button>
|
||||
</form>
|
||||
[[ end ]]
|
||||
</Layout>
|
@ -1,71 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{.ServiceName}}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<link rel="stylesheet" href="/assets/style.css">
|
||||
</head>
|
||||
<body>
|
||||
{{template "header.go.html" .}}
|
||||
<main>
|
||||
<form method="GET" action="/">
|
||||
<button type="submit">Home</button>
|
||||
</form>
|
||||
|
||||
<h2>Manage Users</h2>
|
||||
{{if eq (len .Users) 0}}
|
||||
<div>No users found, this is definitely a bug.</div>
|
||||
{{else}}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Subject</th>
|
||||
<th>Email</th>
|
||||
<th>Email Verified</th>
|
||||
<th>Roles</th>
|
||||
<th>Last Updated</th>
|
||||
<th>Active</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .Users}}
|
||||
<tr>
|
||||
<td>{{.Subject}}</td>
|
||||
<th>
|
||||
{{if $.EmailShow}}
|
||||
<span>{{.Email}}</span>
|
||||
{{else}}
|
||||
<span>{{emailHide .Email}}</span>
|
||||
{{end}}
|
||||
</th>
|
||||
<th>{{.EmailVerified}}</th>
|
||||
<th>{{.Roles}}</th>
|
||||
<th>{{.UpdatedAt}}</th>
|
||||
<td>{{.Active}}</td>
|
||||
<td>
|
||||
<form method="GET" action="/manage/users">
|
||||
<input type="hidden" name="offset" value="{{$.Offset}}"/>
|
||||
<input type="hidden" name="edit" value="{{.Subject}}"/>
|
||||
<button type="submit">Edit</button>
|
||||
</form>
|
||||
<form method="POST" action="/reset-password">
|
||||
<input type="hidden" name="email" value="{{.Email}}"/>
|
||||
<button type="submit">Send Reset Password Email</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
<form method="GET" action="/manage/users">
|
||||
<input type="hidden" name="offset" value="{{.Offset}}"/>
|
||||
{{if not .EmailShow}}
|
||||
<input type="hidden" name="show-email"/>
|
||||
{{end}}
|
||||
<button type="submit">{{if .EmailShow}}Hide Email Addresses{{else}}Show email addresses{{end}}</button>
|
||||
</form>
|
||||
{{end}}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
28
web/src/pages/oauth-authorize.astro
Normal file
28
web/src/pages/oauth-authorize.astro
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<form method="POST" action="/authorize">
|
||||
<div>The application [[ .AppName ]] wants to access your account ([[ .Auth.UserInfo.name ]]). It requests the following permissions:</div>
|
||||
<div>
|
||||
<ul>
|
||||
[[ range .WantsList ]]
|
||||
<li>[[ . ]]</li>
|
||||
[[ end ]]
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<input type="hidden" name="response_type" value="[[ .ResponseType ]]"/>
|
||||
<input type="hidden" name="response_mode" value="[[ .ResponseMode ]]">
|
||||
<input type="hidden" name="client_id" value="[[ .ClientID ]]"/>
|
||||
<input type="hidden" name="redirect_uri" value="[[ .RedirectUri ]]"/>
|
||||
<input type="hidden" name="state" value="[[ .State ]]"/>
|
||||
<input type="hidden" name="scope" value="[[ .Scope ]]"/>
|
||||
<input type="hidden" name="nonce" value="[[ .Nonce ]]"/>
|
||||
<button class="oauth-action-authorize" name="oauth_action" value="authorize">Authorize</button>
|
||||
<button class="oauth-action-cancel" name="oauth_action" value="cancel">Cancel</button>
|
||||
</div>
|
||||
<div>Authorizing this action will redirect you to [[ .AppDomain ]] with access to the permissions requested above.</div>
|
||||
</form>
|
||||
</Layout>
|
@ -1,35 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{.ServiceName}}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="/assets/style.css">
|
||||
</head>
|
||||
<body>
|
||||
{{template "header.go.html" .}}
|
||||
<main>
|
||||
<form method="POST" action="/authorize">
|
||||
<div>The application {{.AppName}} wants to access your account ({{.Auth.UserInfo.name}}). It requests the following permissions:</div>
|
||||
<div>
|
||||
<ul>
|
||||
{{range .WantsList}}
|
||||
<li>{{.}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<input type="hidden" name="response_type" value="{{.ResponseType}}"/>
|
||||
<input type="hidden" name="response_mode" value="{{.ResponseMode}}">
|
||||
<input type="hidden" name="client_id" value="{{.ClientID}}"/>
|
||||
<input type="hidden" name="redirect_uri" value="{{.RedirectUri}}"/>
|
||||
<input type="hidden" name="state" value="{{.State}}"/>
|
||||
<input type="hidden" name="scope" value="{{.Scope}}"/>
|
||||
<input type="hidden" name="nonce" value="{{.Nonce}}"/>
|
||||
<button class="oauth-action-authorize" name="oauth_action" value="authorize">Authorize</button>
|
||||
<button class="oauth-action-cancel" name="oauth_action" value="cancel">Cancel</button>
|
||||
</div>
|
||||
<div>Authorizing this action will redirect you to {{.AppDomain}} with access to the permissions requested above.</div>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
14
web/src/pages/remove-otp.astro
Normal file
14
web/src/pages/remove-otp.astro
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<form method="POST" action="/edit/otp">
|
||||
<input type="hidden" name="remove" value="1"/>
|
||||
<div>
|
||||
<label for="field_code">OTP Code:</label>
|
||||
<input type="text" name="code" id="field_code" required autofocus pattern="[0-9]{6,8}" title="6/7/8 digit one time passcode"/>
|
||||
</div>
|
||||
<button type="submit">Remove OTP</button>
|
||||
</form>
|
||||
</Layout>
|
@ -1,22 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{.ServiceName}}</title>
|
||||
<link rel="stylesheet" href="/theme/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>{{.ServiceName}}</h1>
|
||||
</header>
|
||||
<main>
|
||||
<form method="POST" action="/edit/otp">
|
||||
<input type="hidden" name="remove" value="1"/>
|
||||
<div>
|
||||
<label for="field_code">OTP Code:</label>
|
||||
<input type="text" name="code" id="field_code" required autofocus pattern="[0-9]{6,8}" title="6/7/8 digit one time passcode"/>
|
||||
</div>
|
||||
<button type="submit">Remove OTP</button>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
18
web/src/pages/reset-password.astro
Normal file
18
web/src/pages/reset-password.astro
Normal file
@ -0,0 +1,18 @@
|
||||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<form method="POST" action="/mail/password">
|
||||
<input type="hidden" name="code" value="[[ .Code ]]"/>
|
||||
<div>
|
||||
<label for="field_new_password">New Password:</label>
|
||||
<input type="password" name="new_password" id="field_new_password" autocomplete="new_password" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_confirm_password">Confirm Password:</label>
|
||||
<input type="password" name="confirm_password" id="field_confirm_password" autocomplete="confirm_password" required/>
|
||||
</div>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
</Layout>
|
@ -1,26 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{.ServiceName}}</title>
|
||||
<link rel="stylesheet" href="/theme/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>{{.ServiceName}}</h1>
|
||||
</header>
|
||||
<main>
|
||||
<form method="POST" action="/mail/password">
|
||||
<input type="hidden" name="code" value="{{.Code}}"/>
|
||||
<div>
|
||||
<label for="field_new_password">New Password:</label>
|
||||
<input type="password" name="new_password" id="field_new_password" autocomplete="new_password" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_confirm_password">Confirm Password:</label>
|
||||
<input type="password" name="confirm_password" id="field_confirm_password" autocomplete="confirm_password" required/>
|
||||
</div>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
6
web/svelte.config.js
Normal file
6
web/svelte.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
import {vitePreprocess} from '@astrojs/svelte';
|
||||
|
||||
export default {
|
||||
extensions: ['svelte'],
|
||||
preprocess: vitePreprocess(),
|
||||
}
|
33
web/web.go
33
web/web.go
@ -6,12 +6,12 @@ import (
|
||||
"github.com/1f349/lavender/logger"
|
||||
"github.com/1f349/lavender/utils"
|
||||
"github.com/1f349/overlapfs"
|
||||
"html"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
@ -27,10 +27,7 @@ var (
|
||||
|
||||
func LoadPages(wd string) error {
|
||||
return loadOnce.Do(func() (err error) {
|
||||
webCombinedDir, err = fs.Sub(webBuild, "dist")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
webCombinedDir = webBuild
|
||||
|
||||
if wd != "" {
|
||||
webDir := filepath.Join(wd, "web")
|
||||
@ -44,21 +41,37 @@ func LoadPages(wd string) error {
|
||||
webCombinedDir = overlapfs.OverlapFS{A: webBuild, B: wdFs}
|
||||
}
|
||||
|
||||
// TODO(melon): figure this out layer
|
||||
webCombinedDir = webBuild
|
||||
|
||||
pageTemplates, err = template.New("web").Delims("[[", "]]").Funcs(template.FuncMap{
|
||||
"emailHide": utils.EmailHide,
|
||||
"renderTitle":
|
||||
}).ParseFS(webCombinedDir, "*/index.html")
|
||||
"emailHide": utils.EmailHide,
|
||||
"renderOptionTag": renderOptionTag,
|
||||
"renderCheckboxTag": renderCheckboxTag,
|
||||
}).ParseFS(webCombinedDir, "dist/*.html")
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func renderTitle(title string, service string) string {
|
||||
func renderOptionTag(value, display string, selectedValue string) template.HTML {
|
||||
var selectedParam string
|
||||
if value == selectedValue {
|
||||
selectedParam = " selected"
|
||||
}
|
||||
return template.HTML("<option value=\"" + html.EscapeString(value) + "\"" + selectedParam + ">" + html.EscapeString(display) + "</option>")
|
||||
}
|
||||
|
||||
func renderCheckboxTag(name, id string, checked bool) template.HTML {
|
||||
var checkedParam string
|
||||
if checked {
|
||||
checkedParam = " checked"
|
||||
}
|
||||
return template.HTML("<input type=\"checkbox\" name=\"" + html.EscapeString(name) + "\" id=\"" + html.EscapeString(id) + "\"" + checkedParam + " />")
|
||||
}
|
||||
|
||||
func RenderPageTemplate(wr io.Writer, name string, data any) {
|
||||
p := path.Join(name, "index.html")
|
||||
p := name + ".html"
|
||||
err := pageTemplates.ExecuteTemplate(wr, p, data)
|
||||
if err != nil {
|
||||
logger.Logger.Warn("Failed to render page", "name", name, "err", err)
|
||||
|
28
web/web_test.go
Normal file
28
web/web_test.go
Normal file
@ -0,0 +1,28 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/1f349/lavender/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"html/template"
|
||||
"io/fs"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLoadPages_FindErrors(t *testing.T) {
|
||||
glob, err := fs.Glob(webBuild, "dist/*/index.html")
|
||||
assert.NoError(t, err)
|
||||
|
||||
fmt.Println(glob)
|
||||
|
||||
for _, fileName := range glob {
|
||||
t.Run("Parsing "+fileName, func(t *testing.T) {
|
||||
_, err := template.New("web").Delims("[[", "]]").Funcs(template.FuncMap{
|
||||
"emailHide": utils.EmailHide,
|
||||
"renderOptionTag": renderOptionTag,
|
||||
"renderCheckboxTag": renderCheckboxTag,
|
||||
}).ParseFS(webBuild, fileName)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
208
web/yarn.lock
208
web/yarn.lock
@ -7,7 +7,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30"
|
||||
integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==
|
||||
|
||||
"@ampproject/remapping@^2.2.0":
|
||||
"@ampproject/remapping@^2.2.0", "@ampproject/remapping@^2.2.1":
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4"
|
||||
integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==
|
||||
@ -90,6 +90,14 @@
|
||||
dependencies:
|
||||
prismjs "^1.29.0"
|
||||
|
||||
"@astrojs/svelte@^5.7.3":
|
||||
version "5.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@astrojs/svelte/-/svelte-5.7.3.tgz#2d9ad988798cd6002d3f761cd411f64d5682d756"
|
||||
integrity sha512-0PAwn2KLVpGsJppG8dWM1P9+/VrjAdhMWgEgwC1PjY2xFG565bTw7OuGaS5zh5Fu4AcjVoBkVQKjW/N3mLnrJA==
|
||||
dependencies:
|
||||
"@sveltejs/vite-plugin-svelte" "^3.1.2"
|
||||
svelte2tsx "^0.7.22"
|
||||
|
||||
"@astrojs/tailwind@^5.1.3":
|
||||
version "5.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@astrojs/tailwind/-/tailwind-5.1.3.tgz#80a73c6da46df276fc41cee6042c356c368ecf37"
|
||||
@ -598,7 +606,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
|
||||
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
|
||||
|
||||
"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
|
||||
"@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
|
||||
version "0.3.25"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
|
||||
integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
|
||||
@ -778,6 +786,26 @@
|
||||
resolved "https://registry.yarnpkg.com/@shikijs/vscode-textmate/-/vscode-textmate-9.3.0.tgz#b2f1776e488c1d6c2b6cd129bab62f71bbc9c7ab"
|
||||
integrity sha512-jn7/7ky30idSkd/O5yDBfAnVt+JJpepofP/POZ1iMOxK59cOfqIgg/Dj0eFsjOTMw+4ycJN0uhZH/Eb0bs/EUA==
|
||||
|
||||
"@sveltejs/vite-plugin-svelte-inspector@^2.1.0":
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.1.0.tgz#116ba2b73be43c1d7d93de749f37becc7e45bb8c"
|
||||
integrity sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==
|
||||
dependencies:
|
||||
debug "^4.3.4"
|
||||
|
||||
"@sveltejs/vite-plugin-svelte@^3.1.2":
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.2.tgz#be3120b52e6d9facb55d58392b0dad9e5a35ba6f"
|
||||
integrity sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA==
|
||||
dependencies:
|
||||
"@sveltejs/vite-plugin-svelte-inspector" "^2.1.0"
|
||||
debug "^4.3.4"
|
||||
deepmerge "^4.3.1"
|
||||
kleur "^4.1.5"
|
||||
magic-string "^0.30.10"
|
||||
svelte-hmr "^0.16.0"
|
||||
vitefu "^0.2.5"
|
||||
|
||||
"@types/babel__core@^7.20.5":
|
||||
version "7.20.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017"
|
||||
@ -823,7 +851,7 @@
|
||||
dependencies:
|
||||
"@types/ms" "*"
|
||||
|
||||
"@types/estree@1.0.6", "@types/estree@^1.0.0":
|
||||
"@types/estree@1.0.6", "@types/estree@^1.0.0", "@types/estree@^1.0.1", "@types/estree@^1.0.6":
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
|
||||
integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
|
||||
@ -937,7 +965,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@vscode/l10n/-/l10n-0.0.18.tgz#916d3a5e960dbab47c1c56f58a7cb5087b135c95"
|
||||
integrity sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==
|
||||
|
||||
acorn@^8.14.0:
|
||||
acorn@^8.10.0, acorn@^8.14.0, acorn@^8.9.0:
|
||||
version "8.14.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0"
|
||||
integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
|
||||
@ -1011,7 +1039,7 @@ argparse@^2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
|
||||
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
|
||||
|
||||
aria-query@^5.3.2:
|
||||
aria-query@^5.3.0, aria-query@^5.3.2:
|
||||
version "5.3.2"
|
||||
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59"
|
||||
integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==
|
||||
@ -1103,7 +1131,7 @@ autoprefixer@^10.4.20:
|
||||
picocolors "^1.0.1"
|
||||
postcss-value-parser "^4.2.0"
|
||||
|
||||
axobject-query@^4.1.0:
|
||||
axobject-query@^4.0.0, axobject-query@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee"
|
||||
integrity sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==
|
||||
@ -1269,6 +1297,17 @@ clsx@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
|
||||
integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
|
||||
|
||||
code-red@^1.0.3:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/code-red/-/code-red-1.0.4.tgz#59ba5c9d1d320a4ef795bc10a28bd42bfebe3e35"
|
||||
integrity sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==
|
||||
dependencies:
|
||||
"@jridgewell/sourcemap-codec" "^1.4.15"
|
||||
"@types/estree" "^1.0.1"
|
||||
acorn "^8.10.0"
|
||||
estree-walker "^3.0.3"
|
||||
periscopic "^3.1.0"
|
||||
|
||||
color-convert@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
|
||||
@ -1331,6 +1370,14 @@ cross-spawn@^7.0.0:
|
||||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
css-tree@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20"
|
||||
integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==
|
||||
dependencies:
|
||||
mdn-data "2.0.30"
|
||||
source-map-js "^1.0.1"
|
||||
|
||||
cssesc@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
|
||||
@ -1350,6 +1397,16 @@ decode-named-character-reference@^1.0.0:
|
||||
dependencies:
|
||||
character-entities "^2.0.0"
|
||||
|
||||
dedent-js@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/dedent-js/-/dedent-js-1.0.1.tgz#bee5fb7c9e727d85dffa24590d10ec1ab1255305"
|
||||
integrity sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==
|
||||
|
||||
deepmerge@^4.3.1:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
|
||||
integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==
|
||||
|
||||
dequal@^2.0.0:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be"
|
||||
@ -1496,7 +1553,7 @@ estree-walker@^2.0.2:
|
||||
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
|
||||
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
|
||||
|
||||
estree-walker@^3.0.3:
|
||||
estree-walker@^3.0.0, estree-walker@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d"
|
||||
integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==
|
||||
@ -1882,6 +1939,13 @@ is-plain-obj@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0"
|
||||
integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==
|
||||
|
||||
is-reference@^3.0.0, is-reference@^3.0.1:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-3.0.3.tgz#9ef7bf9029c70a67b2152da4adf57c23d718910f"
|
||||
integrity sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==
|
||||
dependencies:
|
||||
"@types/estree" "^1.0.6"
|
||||
|
||||
is-unicode-supported@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz#d824984b616c292a2e198207d4a609983842f714"
|
||||
@ -1998,6 +2062,11 @@ load-yaml-file@^0.2.0:
|
||||
pify "^4.0.1"
|
||||
strip-bom "^3.0.0"
|
||||
|
||||
locate-character@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-character/-/locate-character-3.0.0.tgz#0305c5b8744f61028ef5d01f444009e00779f974"
|
||||
integrity sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==
|
||||
|
||||
locate-path@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
|
||||
@ -2023,6 +2092,13 @@ longest-streak@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4"
|
||||
integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==
|
||||
|
||||
lower-case@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
|
||||
integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==
|
||||
dependencies:
|
||||
tslib "^2.0.3"
|
||||
|
||||
lru-cache@^10.2.0:
|
||||
version "10.4.3"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
|
||||
@ -2035,6 +2111,13 @@ lru-cache@^5.1.1:
|
||||
dependencies:
|
||||
yallist "^3.0.2"
|
||||
|
||||
magic-string@^0.30.10, magic-string@^0.30.4:
|
||||
version "0.30.17"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453"
|
||||
integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==
|
||||
dependencies:
|
||||
"@jridgewell/sourcemap-codec" "^1.5.0"
|
||||
|
||||
magic-string@^0.30.14:
|
||||
version "0.30.14"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.14.tgz#e9bb29870b81cfc1ec3cc656552f5a7fcbf19077"
|
||||
@ -2203,6 +2286,11 @@ mdast-util-to-string@^4.0.0:
|
||||
dependencies:
|
||||
"@types/mdast" "^4.0.0"
|
||||
|
||||
mdn-data@2.0.30:
|
||||
version "2.0.30"
|
||||
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc"
|
||||
integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==
|
||||
|
||||
merge2@^1.3.0:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
|
||||
@ -2547,6 +2635,14 @@ nlcst-to-string@^4.0.0:
|
||||
dependencies:
|
||||
"@types/nlcst" "^2.0.0"
|
||||
|
||||
no-case@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
|
||||
integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==
|
||||
dependencies:
|
||||
lower-case "^2.0.2"
|
||||
tslib "^2.0.3"
|
||||
|
||||
node-releases@^2.0.18:
|
||||
version "2.0.18"
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f"
|
||||
@ -2666,6 +2762,14 @@ parse5@^7.0.0:
|
||||
dependencies:
|
||||
entities "^4.5.0"
|
||||
|
||||
pascal-case@^3.1.1:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb"
|
||||
integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==
|
||||
dependencies:
|
||||
no-case "^3.0.4"
|
||||
tslib "^2.0.3"
|
||||
|
||||
path-browserify@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
|
||||
@ -2694,6 +2798,15 @@ path-scurry@^1.11.1:
|
||||
lru-cache "^10.2.0"
|
||||
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||
|
||||
periscopic@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/periscopic/-/periscopic-3.1.0.tgz#7e9037bf51c5855bd33b48928828db4afa79d97a"
|
||||
integrity sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==
|
||||
dependencies:
|
||||
"@types/estree" "^1.0.0"
|
||||
estree-walker "^3.0.0"
|
||||
is-reference "^3.0.0"
|
||||
|
||||
picocolors@^1.0.0, picocolors@^1.0.1, picocolors@^1.1.0, picocolors@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
|
||||
@ -3149,7 +3262,7 @@ sisteransi@^1.0.5:
|
||||
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
|
||||
integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
|
||||
|
||||
source-map-js@^1.2.0, source-map-js@^1.2.1:
|
||||
source-map-js@^1.0.1, source-map-js@^1.2.0, source-map-js@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
|
||||
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
||||
@ -3169,16 +3282,7 @@ stdin-discarder@^0.2.2:
|
||||
resolved "https://registry.yarnpkg.com/stdin-discarder/-/stdin-discarder-0.2.2.tgz#390037f44c4ae1a1ae535c5fe38dc3aba8d997be"
|
||||
integrity sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==
|
||||
|
||||
"string-width-cjs@npm:string-width@^4.2.0":
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
dependencies:
|
||||
emoji-regex "^8.0.0"
|
||||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.1"
|
||||
|
||||
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
@ -3213,14 +3317,7 @@ stringify-entities@^4.0.0:
|
||||
character-entities-html4 "^2.0.0"
|
||||
character-entities-legacy "^3.0.0"
|
||||
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
dependencies:
|
||||
ansi-regex "^5.0.1"
|
||||
|
||||
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
@ -3262,6 +3359,39 @@ supports-preserve-symlinks-flag@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||
|
||||
svelte-hmr@^0.16.0:
|
||||
version "0.16.0"
|
||||
resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.16.0.tgz#9f345b7d1c1662f1613747ed7e82507e376c1716"
|
||||
integrity sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==
|
||||
|
||||
svelte2tsx@^0.7.22:
|
||||
version "0.7.33"
|
||||
resolved "https://registry.yarnpkg.com/svelte2tsx/-/svelte2tsx-0.7.33.tgz#e87a42cb88f9611b2d22fd3f74a0f48035ff8f4c"
|
||||
integrity sha512-geogGkzfciwteiKvlbaDBnKOitWuh6e1n2f5KLBBXEfZgui9gy5yRlOBYtNEkdwciO4MC9fTM/EyltsiQrOPNQ==
|
||||
dependencies:
|
||||
dedent-js "^1.0.1"
|
||||
pascal-case "^3.1.1"
|
||||
|
||||
svelte@^4.2.19:
|
||||
version "4.2.19"
|
||||
resolved "https://registry.yarnpkg.com/svelte/-/svelte-4.2.19.tgz#4e6e84a8818e2cd04ae0255fcf395bc211e61d4c"
|
||||
integrity sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==
|
||||
dependencies:
|
||||
"@ampproject/remapping" "^2.2.1"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.15"
|
||||
"@jridgewell/trace-mapping" "^0.3.18"
|
||||
"@types/estree" "^1.0.1"
|
||||
acorn "^8.9.0"
|
||||
aria-query "^5.3.0"
|
||||
axobject-query "^4.0.0"
|
||||
code-red "^1.0.3"
|
||||
css-tree "^2.3.1"
|
||||
estree-walker "^3.0.3"
|
||||
is-reference "^3.0.1"
|
||||
locate-character "^3.0.0"
|
||||
magic-string "^0.30.4"
|
||||
periscopic "^3.1.0"
|
||||
|
||||
tailwindcss@^3.4.16:
|
||||
version "3.4.16"
|
||||
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.16.tgz#35a7c3030844d6000fc271878db4096b6a8d2ec9"
|
||||
@ -3336,7 +3466,7 @@ tsconfck@^3.1.4:
|
||||
resolved "https://registry.yarnpkg.com/tsconfck/-/tsconfck-3.1.4.tgz#de01a15334962e2feb526824339b51be26712229"
|
||||
integrity sha512-kdqWFGVJqe+KGYvlSO9NIaWn9jT1Ny4oKVzAJsKii5eoE9snzTJzL4+MMVOMn+fikWGFmKEylcXL710V/kIPJQ==
|
||||
|
||||
tslib@^2.4.0:
|
||||
tslib@^2.0.3, tslib@^2.4.0:
|
||||
version "2.8.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
|
||||
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
|
||||
@ -3358,10 +3488,10 @@ typescript-auto-import-cache@^0.3.3:
|
||||
dependencies:
|
||||
semver "^7.3.8"
|
||||
|
||||
typescript@^5.7.2:
|
||||
version "5.7.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6"
|
||||
integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==
|
||||
typescript@^5.7.3:
|
||||
version "5.7.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.3.tgz#919b44a7dbb8583a9b856d162be24a54bf80073e"
|
||||
integrity sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==
|
||||
|
||||
unified@^11.0.0, unified@^11.0.4, unified@^11.0.5:
|
||||
version "11.0.5"
|
||||
@ -3493,6 +3623,11 @@ vite@^5.4.11:
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.3"
|
||||
|
||||
vitefu@^0.2.5:
|
||||
version "0.2.5"
|
||||
resolved "https://registry.yarnpkg.com/vitefu/-/vitefu-0.2.5.tgz#c1b93c377fbdd3e5ddd69840ea3aa70b40d90969"
|
||||
integrity sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==
|
||||
|
||||
vitefu@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/vitefu/-/vitefu-1.0.4.tgz#8e0355362d2f64c499cbb22d5dbc3184d02c9a2d"
|
||||
@ -3687,16 +3822,7 @@ widest-line@^5.0.0:
|
||||
dependencies:
|
||||
string-width "^7.0.0"
|
||||
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
dependencies:
|
||||
ansi-styles "^4.0.0"
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrap-ansi@^7.0.0:
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
|
Loading…
Reference in New Issue
Block a user