mirror of
https://github.com/Blah-IM/Weblah.git
synced 2025-05-01 00:31:08 +00:00
feat: domain setup
This commit is contained in:
parent
b3e16f2063
commit
833460d709
5 changed files with 75 additions and 30 deletions
21
src/lib/components/Card.svelte
Normal file
21
src/lib/components/Card.svelte
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { tw } from '$lib/tw';
|
||||||
|
import type { HTMLAttributes } from 'svelte/elements';
|
||||||
|
|
||||||
|
interface Props extends HTMLAttributes<HTMLDivElement> {
|
||||||
|
class?: string;
|
||||||
|
children?: import('svelte').Snippet;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { children, class: classNames, ...rest }: Props = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class={tw(
|
||||||
|
'border-ss-secondary bg-sb-primary overflow-hidden rounded-lg border-[0.5px] shadow-xs',
|
||||||
|
classNames
|
||||||
|
)}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
|
@ -1,4 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import Card from '../Card.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
header?: import('svelte').Snippet | string;
|
header?: import('svelte').Snippet | string;
|
||||||
children?: import('svelte').Snippet;
|
children?: import('svelte').Snippet;
|
||||||
|
@ -18,11 +20,9 @@
|
||||||
{/if}
|
{/if}
|
||||||
</h3>
|
</h3>
|
||||||
{/if}
|
{/if}
|
||||||
<div
|
<Card class="divide-ss-secondary divide-y-[0.5px]">
|
||||||
class="divide-ss-secondary border-ss-secondary bg-sb-primary divide-y-[0.5px] overflow-hidden rounded-lg border-[0.5px] shadow-xs"
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</div>
|
</Card>
|
||||||
{#if footer}
|
{#if footer}
|
||||||
<div class="text-sf-tertiary mt-1 px-4 text-sm">
|
<div class="text-sf-tertiary mt-1 px-4 text-sm">
|
||||||
{#if typeof footer === 'string'}
|
{#if typeof footer === 'string'}
|
||||||
|
|
|
@ -9,6 +9,8 @@ export function idURLToUsername(idURL: string): string {
|
||||||
return url.host;
|
return url.host;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const identityDescriptionFilePath = '/.well-known/blah/identity.json';
|
||||||
|
|
||||||
export type IDURLValidity =
|
export type IDURLValidity =
|
||||||
| { valid: true }
|
| { valid: true }
|
||||||
| ({ valid: false } & (
|
| ({ valid: false } & (
|
||||||
|
@ -39,7 +41,7 @@ export async function validateIDURL(url: string, identity: BlahIdentity): Promis
|
||||||
|
|
||||||
const profileFileURL = (() => {
|
const profileFileURL = (() => {
|
||||||
let url = idURL;
|
let url = idURL;
|
||||||
url.pathname = '/.well-known/blah/identity.json';
|
url.pathname = identityDescriptionFilePath;
|
||||||
return url.toString();
|
return url.toString();
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Dialog from '$lib/components/Dialog.svelte';
|
|
||||||
import { GroupedListItem } from '$lib/components/GroupedList';
|
import { GroupedListItem } from '$lib/components/GroupedList';
|
||||||
import { idURLToUsername, validateIDURL, type IDURLValidity } from '$lib/idURL';
|
import { idURLToUsername, validateIDURL, type IDURLValidity } from '$lib/idURL';
|
||||||
import type { BlahIdentity } from '@blah-im/core/identity';
|
import type { BlahIdentity } from '@blah-im/core/identity';
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Button from '$lib/components/Button.svelte';
|
import Button from '$lib/components/Button.svelte';
|
||||||
|
import Card from '$lib/components/Card.svelte';
|
||||||
import Dialog from '$lib/components/Dialog.svelte';
|
import Dialog from '$lib/components/Dialog.svelte';
|
||||||
import { GroupedListContainer, GroupedListSection } from '$lib/components/GroupedList';
|
import Link from '$lib/components/Link.svelte';
|
||||||
import GroupedListContent from '$lib/components/GroupedList/GroupedListContent.svelte';
|
|
||||||
import PageHeader from '$lib/components/PageHeader.svelte';
|
import PageHeader from '$lib/components/PageHeader.svelte';
|
||||||
import { idURLToUsername } from '$lib/idURL';
|
import { identityDescriptionFilePath, idURLToUsername } from '$lib/idURL';
|
||||||
import type { BlahIdentity } from '@blah-im/core/identity';
|
import type { BlahIdentity } from '@blah-im/core/identity';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
@ -19,6 +19,26 @@
|
||||||
const profileDescriptionString = $derived(
|
const profileDescriptionString = $derived(
|
||||||
JSON.stringify(identity.generateIdentityDescription(), null, 2)
|
JSON.stringify(identity.generateIdentityDescription(), null, 2)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let copied = $state(false);
|
||||||
|
let jsonFileBlobHref: string | null = $state(null);
|
||||||
|
|
||||||
|
const copyToClipboard = async () => {
|
||||||
|
await navigator.clipboard.writeText(profileDescriptionString);
|
||||||
|
copied = true;
|
||||||
|
setTimeout(() => (copied = false), 2000);
|
||||||
|
};
|
||||||
|
|
||||||
|
$effect.pre(() => {
|
||||||
|
if (identity) {
|
||||||
|
const blob = new Blob([profileDescriptionString], { type: 'application/json' });
|
||||||
|
jsonFileBlobHref = URL.createObjectURL(blob);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (jsonFileBlobHref) URL.revokeObjectURL(jsonFileBlobHref);
|
||||||
|
};
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Dialog bind:open class="flex h-2/3 flex-col">
|
<Dialog bind:open class="flex h-2/3 flex-col">
|
||||||
|
@ -27,25 +47,28 @@
|
||||||
<Button variant="primary" onclick={() => (open = false)}>Done</Button>
|
<Button variant="primary" onclick={() => (open = false)}>Done</Button>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<GroupedListContainer class="w-full grow overflow-x-auto">
|
<div class="flex grow flex-col gap-3 p-3">
|
||||||
<GroupedListSection>
|
<p class="text-sf-primary px-4 text-sm">
|
||||||
{#snippet header()}
|
For others to validate ownership of your domain, make text file below available as
|
||||||
<div class="-me-4 flex min-w-0 items-end gap-2 text-base normal-case">
|
<code>{identityDescriptionFilePath}</code>
|
||||||
<p class="text-sf-primary">
|
under your domain, and make sure it allows any cross domain requests.
|
||||||
For others to validate your domain as your username, put the content below at
|
<Link href="/">Learn more...</Link>
|
||||||
<code>/.well-known/blah/profile.json</code>
|
</p>
|
||||||
under your domain.
|
<Card class="relative grow">
|
||||||
</p>
|
<textarea
|
||||||
<Button>Copy</Button>
|
readonly
|
||||||
</div>
|
class="text-sf-primary block h-full w-full resize-none overflow-x-auto px-4 py-3 font-mono text-sm"
|
||||||
{/snippet}
|
value={profileDescriptionString}
|
||||||
<GroupedListContent class="p-0">
|
></textarea>
|
||||||
<textarea
|
|
||||||
readonly
|
<div class="absolute end-2 top-2">
|
||||||
class="text-sf-primary block h-100 w-full resize-none overflow-x-auto px-4 py-3 font-mono"
|
<Button onclick={copyToClipboard}>
|
||||||
value={profileDescriptionString}
|
{#if copied}Copied!{:else}Copy{/if}
|
||||||
></textarea>
|
</Button>
|
||||||
</GroupedListContent>
|
{#if jsonFileBlobHref}
|
||||||
</GroupedListSection>
|
<Button href={jsonFileBlobHref} download="identity.json">Download</Button>
|
||||||
</GroupedListContainer>
|
{/if}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
Loading…
Add table
Reference in a new issue