Merge pull request #339 from cloudflare/migration-ui

Migration UI
pull/349/head
Sven Sauleau 2023-02-28 09:34:42 +00:00 zatwierdzone przez GitHub
commit a97dbfa814
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
9 zmienionych plików z 338 dodań i 2 usunięć

Wyświetl plik

@ -1,6 +1,7 @@
import { component$ } from '@builder.io/qwik'
import { Link, useLocation } from '@builder.io/qwik-city'
import { WildebeestLogo } from '~/components/MastodonLogo'
import { accessLoader } from '~/routes/layout'
type LinkConfig = {
iconName: string
@ -10,6 +11,7 @@ type LinkConfig = {
}
export default component$(() => {
const accessData = accessLoader.use().value
const location = useLocation()
const renderNavLink = ({ iconName, linkText, linkTarget, linkActiveRegex }: LinkConfig) => {
@ -52,6 +54,21 @@ export default component$(() => {
<hr class="border-t border-wildebeest-700 my-3" />
{renderNavLink(aboutLink)}
</div> */}
{!accessData.isAuthorized && (
<a
class="w-full block mb-4 no-underline text-center bg-wildebeest-vibrant-600 hover:bg-wildebeest-vibrant-500 p-2 text-white text-uppercase border-wildebeest-vibrant-600 text-lg text-semi outline-none border rounded hover:border-wildebeest-vibrant-500 focus:border-wildebeest-vibrant-500"
href={accessData.loginUrl}
>
Sign in
</a>
)}
{accessData.isAuthorized && (
<a class="text-semi no-underline" href="/settings/migration">
<i class="fa fa-gear mx-3 w-4" />
Preferences
</a>
)}
</div>
</div>
)

Wyświetl plik

@ -0,0 +1,108 @@
import { component$, useStore, useSignal, $ } from '@builder.io/qwik'
import { loader$ } from '@builder.io/qwik-city'
import { WildebeestEnv } from '~/types'
import { checkAuth } from '~/utils/checkAuth'
export const loader = loader$<WildebeestEnv, void>(async ({ request, platform, redirect }) => {
const isAuthorized = await checkAuth(request, platform)
if (!isAuthorized) {
redirect(303, '/explore')
}
})
export default component$(() => {
const ref = useSignal<Element>()
const state = useStore({ alias: '' })
const toast = useSignal<'success' | 'failure' | null>(null)
const handleInput = $((event: Event) => {
state.alias = (event.target as HTMLInputElement).value
})
const handleSubmit = $(async () => {
const res = await fetch('/api/wb/settings/account/alias', { method: 'POST', body: JSON.stringify(state) })
if (res.status == 200) {
toast.value = 'success'
} else {
toast.value = 'failure'
}
})
return (
<form ref={ref} class="login-form" preventdefault:submit onSubmit$={handleSubmit}>
<div class="max-w-4xl py-14 px-8">
<h2 class="text-2xl font-bold mb-6">Account Aliases</h2>
{toast.value === 'success' && (
<div class="bg-green-800 border-green-700 text-green-100 border mb-5 p-5 text-center rounded">
Successfully created a new alias. You can now initiate the move from the old account.
</div>
)}
{toast.value === 'failure' && (
<div class="bg-red-800 border-red-700 text-red-100 border mb-5 p-5 text-center rounded">
Failed to create alias.
</div>
)}
<p class="text-sm text-wildebeest-400 mb-10">
If you want to move from another account to this one, here you can create an alias, which is required before
you can proceed with moving followers from the old account to this one. This action by itself is harmless and
reversible. The account migration is initiated from the old account.
</p>
<div class="my-5">
<label class="font-semibold mb-3" for="alias">
Handle of the old account
<span class="ml-1 text-red-500">*</span>
</label>
<div class="text-sm text-wildebeest-400">
Specify the username@domain of the account you want to move from
</div>
</div>
<input
class="bg-black text-white p-3 rounded outline-none border border-black hover:border-wildebeest-vibrant-500 focus:border-wildebeest-vibrant-500 w-full mb-5"
type="text"
id="alias"
name="alias"
value={state.alias}
onInput$={handleInput}
/>
<button
type="submit"
class="w-full uppercase mb-9 bg-wildebeest-vibrant-600 hover:bg-wildebeest-vibrant-500 p-2 text-white text-uppercase border-wildebeest-vibrant-600 text-lg text-semi outline-none border rounded hover:border-wildebeest-vibrant-500 focus:border-wildebeest-vibrant-500"
>
Create Alias
</button>
{/* <table class="table-auto w-full">
<thead class="border-gray-600 border-b-2">
<th class="text-left py-2">Handle of the old account</th>
<th></th>
</thead>
<tbody>
<tr class="border-gray-600 border-t">
<td class="py-2">test</td>
<td class="py-2">
<div class="text-wildebeest-400 hover:text-white cursor-pointer">
<i class="fa fa-trash fa-fw fa-xs mr-1" />
Unlink Alias
</div>
</td>
</tr>
<tr class="border-gray-600 border-t">
<td class="py-2">test 2</td>
<td class="py-2">
<div class="text-wildebeest-400 hover:text-white cursor-pointer">
<i class="fa fa-trash fa-fw fa-xs mr-1" />
Unlink Alias
</div>
</td>
</tr>
</tbody>
</table> */}
</div>
</form>
)
})

Wyświetl plik

@ -0,0 +1,6 @@
import { component$ } from '@builder.io/qwik'
export default component$(() => {
// In the future, a settings homepage will be here
return <div></div>
})

Wyświetl plik

@ -0,0 +1,42 @@
import { component$, Slot } from '@builder.io/qwik'
import { WildebeestLogo } from '~/components/MastodonLogo'
export default component$(() => {
return (
<div class="flex w-screen min-h-screen justify-center">
<AccountSidebar />
<div class="flex-auto">
<Slot />
</div>
<div class="flex-auto" />
</div>
)
})
export const AccountSidebar = component$(() => {
return (
<div class="bg-wildebeest-800 min-h-full flex-auto">
<div class="flex flex-col items-end">
<div class="my-12 mr-6">
<WildebeestLogo size="large" />
</div>
<a class="text-semi no-underline text-wildebeest-vibrant-400 bg-transparent p-4" href="/">
<i class="fa fa-chevron-left mr-2 w-3 inline-block" />
<span class="hover:underline">Back to Wildebeest</span>
</a>
<ul class="mr-5">
{/* <li class="mb-3">
<a class="no-underline text-right text-wildebeest-400 hover:text-wildebeest-200" href="/settings/migration">
Account Migration
</a>
</li> */}
<li class="mb-3">
<a class="no-underline text-right text-wildebeest-400 hover:text-wildebeest-200" href="/settings/aliases">
Account Aliases
</a>
</li>
</ul>
</div>
</div>
)
})

Wyświetl plik

@ -0,0 +1,98 @@
import { component$ } from '@builder.io/qwik'
import { loader$ } from '@builder.io/qwik-city'
import { WildebeestEnv } from '~/types'
// import { checkAuth } from '~/utils/checkAuth'
export const loader = loader$<WildebeestEnv, void>(async ({ redirect }) => {
// Hiding this page for now
redirect(303, '/explore')
// const isAuthorized = await checkAuth(request, platform)
// if (!isAuthorized) {
// redirect(303, '/explore')
// }
})
export default component$(() => {
return (
<div class="max-w-4xl py-14 px-8">
<h2 class="text-2xl font-bold mb-10">Account Migration</h2>
<div class="text-green-700 mb-10">Your account is not currently being redirected to any other account.</div>
<h3 class="text-xl mb-6">Move to a different account</h3>
<p class="text-sm text-wildebeest-400 mb-5">Before proceeding, please read these notes carefully:</p>
<ul class="list-disc list-inside text-sm text-yellow-500 mb-5">
<li class="pb-1">This action will move all followers from the current account to the new account</li>
<li class="pb-1">
Your current account's profile will be updated with a redirect notice and be excluded from searches
</li>
<li class="pb-1">No other data will be moved automatically</li>
<li class="pb-1">The new account must first be configured to back-reference this one</li>
<li class="pb-1">After moving there is a waiting period during which you will not be able to move again</li>
<li class="pb-1">
Your current account will not be fully usable afterwards. However, you will have access to data export as well
as re-activation.
</li>
</ul>
<p class="text-sm text-wildebeest-400 mb-10">
Alternatively, you can <a href="/settings/aliases">only put up a redirect on your profile. </a>
</p>
<div class="flex">
<div class="pr-3">
<div class="my-5">
<label class="font-semibold mb-3" for="old-account">
Handle of the new account
<span class="ml-1 text-red-500">*</span>
</label>
<div class="text-sm text-wildebeest-400">
Specify the username@domain of the account you want to move to
</div>
</div>
<input
class="bg-black text-white p-3 rounded outline-none border border-black hover:border-wildebeest-vibrant-500 focus:border-wildebeest-vibrant-500 w-full mb-5"
type="text"
name="old-account"
id="old-account"
/>
</div>
<div class="pl-3">
<div class="my-5">
<label class="font-semibold mb-3" for="password">
Current Password
<span class="ml-1 text-red-500">*</span>
</label>
<div class="text-sm text-wildebeest-400">
For security purposes please enter the password of the current account
</div>
</div>
<input
class="bg-black text-white p-3 rounded outline-none border border-red-500 hover:border-wildebeest-vibrant-500 focus:border-wildebeest-vibrant-500 w-full mb-5"
type="password"
name="password"
id="password"
/>
</div>
</div>
<button
type="submit"
class="w-full mb-10 uppercase bg-wildebeest-vibrant-600 hover:bg-wildebeest-vibrant-500 p-2 text-white text-uppercase border-wildebeest-vibrant-600 text-lg text-semi outline-none border rounded hover:border-wildebeest-vibrant-500 focus:border-wildebeest-vibrant-500"
>
Create Alias
</button>
<h3 class="text-xl mt-4 mb-8">Moving from a different account</h3>
<p class="text-sm text-wildebeest-400 mb-5">
To move from another account to this one, first you need to{' '}
<a href="/settings/aliases">create an account alias</a>.
</p>
</div>
)
})

Wyświetl plik

@ -0,0 +1,31 @@
import { component$, Slot } from '@builder.io/qwik'
import { loader$ } from '@builder.io/qwik-city'
import * as access from 'wildebeest/backend/src/access'
import { WildebeestEnv } from '~/types'
import { checkAuth } from '~/utils/checkAuth'
type AccessLoaderData = {
loginUrl: string
isAuthorized: boolean
}
export const accessLoader = loader$<WildebeestEnv, Promise<AccessLoaderData>>(async ({ platform, request }) => {
const isAuthorized = await checkAuth(request, platform)
return {
isAuthorized,
loginUrl: access.generateLoginURL({
redirectURL: request.url,
domain: platform.ACCESS_AUTH_DOMAIN,
aud: platform.ACCESS_AUD,
}),
}
})
export default component$(() => {
return (
<>
<Slot />
</>
)
})

Wyświetl plik

@ -216,3 +216,8 @@ export type History = {
accounts: string
uses: string
}
export type WildebeestEnv = {
ACCESS_AUTH_DOMAIN: string
ACCESS_AUD: string
}

Wyświetl plik

@ -0,0 +1,29 @@
import { RequestContext } from '@builder.io/qwik-city/middleware/request-handler'
import * as access from 'wildebeest/backend/src/access'
type Env = {
ACCESS_AUTH_DOMAIN: string
ACCESS_AUD: string
}
export const checkAuth = async (request: RequestContext, platform: Env) => {
const jwt = request.headers.get('Cf-Access-Jwt-Assertion') || ''
if (!jwt) return false
try {
const validate = access.generateValidator({
jwt,
domain: platform.ACCESS_AUTH_DOMAIN,
aud: platform.ACCESS_AUD,
})
await validate(new Request(request.url))
} catch {
return false
}
const identity = await access.getIdentity({ jwt, domain: platform.ACCESS_AUTH_DOMAIN })
if (identity) {
return true
}
return false
}

Wyświetl plik

@ -38,8 +38,8 @@
"pages": "NO_D1_WARNING=true wrangler pages",
"database:migrate": "yarn d1 migrations apply DATABASE",
"database:create-mock": "rm -f .wrangler/state/d1/DATABASE.sqlite3 && CI=true yarn database:migrate --local && node ./frontend/mock-db/run.mjs",
"dev": "export COMMIT_HASH=$(git rev-parse HEAD) && yarn build && yarn database:migrate --local && yarn pages dev frontend/dist --d1 DATABASE --persist --compatibility-date=2022-12-20 --binding 'INSTANCE_TITLE=Test Wildebeest' 'INSTANCE_DESCR=My Wildebeest Instance' --live-reload",
"ci-dev-test-ui": "yarn build && yarn database:create-mock && yarn pages dev frontend/dist --d1 DATABASE --persist --port 8788 --binding 'INSTANCE_TITLE=Test Wildebeest' 'INSTANCE_DESCR=My Wildebeest Instance' --compatibility-date=2022-12-20",
"dev": "export COMMIT_HASH=$(git rev-parse HEAD) && yarn build && yarn database:migrate --local && yarn pages dev frontend/dist --d1 DATABASE --persist --compatibility-date=2022-12-20 --binding 'INSTANCE_TITLE=Test Wildebeest' 'INSTANCE_DESCR=My Wildebeest Instance' 'ACCESS_AUTH_DOMAIN=0.0.0.0.cloudflareaccess.com' 'ACCESS_AUD=DEV_AUD' --live-reload",
"ci-dev-test-ui": "yarn build && yarn database:create-mock && yarn pages dev frontend/dist --d1 DATABASE --persist --port 8788 --binding 'INSTANCE_TITLE=Test Wildebeest' 'INSTANCE_DESCR=My Wildebeest Instance' 'ACCESS_AUTH_DOMAIN=0.0.0.0.cloudflareaccess.com' 'ACCESS_AUD=DEV_AUD' --compatibility-date=2022-12-20",
"deploy:init": "yarn pages project create wildebeest && yarn d1 create wildebeest",
"deploy": "yarn build && yarn database:migrate && yarn pages publish frontend/dist --project-name=wildebeest"
},