fix: Move more directories

This commit is contained in:
Wanjohi
2025-09-06 16:50:44 +03:00
parent 1c1c73910b
commit 9818165a90
248 changed files with 9 additions and 9566 deletions

View File

@@ -0,0 +1,481 @@
/** @jsxImportSource hono/jsx */
import {
type PasswordChangeError,
type PasswordConfig,
type PasswordLoginError,
type PasswordRegisterError,
} from "../adapters"
// import { Layout } from "@openauthjs/openauth/ui/base"
import { Layout } from "./base"
import "@openauthjs/openauth/ui/form"
// import { FormAlert } from "@openauthjs/openauth/ui/form"
const DEFAULT_COPY = {
error_email_taken: "There is already an account with this email.",
error_username_taken: "There is already an account with this username.",
error_invalid_code: "Code is incorrect.",
error_invalid_email: "Email is not valid.",
error_invalid_password: "Password is incorrect.",
error_invalid_username: "Username can only contain letters.",
error_password_mismatch: "Passwords do not match.",
register_title: "Welcome to the app",
register_description: "Sign in with your email",
login_title: "Welcome to the app",
login_description: "Sign in with your email",
register: "Register",
register_prompt: "Don't have an account?",
login_prompt: "Already have an account?",
login: "Login",
change_prompt: "Forgot your password?",
change: "Well that sucks",
code_resend: "Resend code",
code_return: "Back to",
logo: "A",
input_email: "john@doe.com",
input_password: "●●●●●●●●●●●",
input_code: "●●●●●●",
input_username: "john",
input_repeat: "●●●●●●●●●●●",
button_continue: "Continue",
} satisfies {
[key in `error_${| PasswordLoginError["type"]
| PasswordRegisterError["type"]
| PasswordChangeError["type"]}`]: string
} & Record<string, string>
export type PasswordUICopy = typeof DEFAULT_COPY
export interface PasswordUIOptions {
sendCode: PasswordConfig["sendCode"]
copy?: Partial<PasswordUICopy>
}
export function PasswordUI(input: PasswordUIOptions) {
const copy = {
...DEFAULT_COPY,
...input.copy,
}
return {
sendCode: input.sendCode,
login: async (_req, form, error): Promise<Response> => {
const emailError = ["invalid_email", "email_taken"].includes(
error?.type || "",
)
const passwordError = ["invalid_password", "password_mismatch"].includes(
error?.type || "",
)
const jsx = (
<Layout page="password">
<div data-component="form-header">
<h1>Login</h1>
<span>
{copy.register_prompt}{" "}
<a data-component="link" href="register">
{copy.register}
</a>
</span>
<hr />
</div>
<form data-component="form" method="post">
{/* <FormAlert message={error?.type && copy?.[`error_${error.type}`]} /> */}
<div
data-component="input-container"
>
<span>Email</span>
<div data-error={emailError} data-component="input-wrapper">
<span data-component="input-icon">
<svg width="24px" height="24px" stroke-width="1.5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="currentColor">
<path d="M7 9l5 3.5L17 9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
</path>
<path d="M2 17V7a2 2 0 012-2h16a2 2 0 012 2v10a2 2 0 01-2 2H4a2 2 0 01-2-2z" stroke="currentColor" stroke-width="1.5">
</path>
</svg>
</span>
<input
data-component="input"
type="email"
name="email"
required
placeholder={copy.input_email}
autofocus={!error}
value={form?.get("email")?.toString()}
/>
</div>
<small>{error?.type && emailError && copy?.[`error_${error.type}`]}</small>
</div>
<div
data-component="input-container"
>
<span>Password</span>
<div data-error={passwordError} data-component="input-wrapper">
<span data-component="input-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" color="currentColor"><path d="M12 16.5v-2m-7.732 4.345c.225 1.67 1.608 2.979 3.292 3.056c1.416.065 2.855.099 4.44.099s3.024-.034 4.44-.1c1.684-.076 3.067-1.385 3.292-3.055c.147-1.09.268-2.207.268-3.345s-.121-2.255-.268-3.345c-.225-1.67-1.608-2.979-3.292-3.056A95 95 0 0 0 12 9c-1.585 0-3.024.034-4.44.1c-1.684.076-3.067 1.385-3.292 3.055C4.12 13.245 4 14.362 4 15.5s.121 2.255.268 3.345" /><path d="M7.5 9V6.5a4.5 4.5 0 0 1 9 0V9" /></g></svg>
</span>
<input
data-component="input"
autofocus={error?.type === "invalid_password"}
required
type="password"
name="password"
placeholder={copy.input_password}
autoComplete="current-password"
/>
</div>
<small>{error?.type && passwordError && copy?.[`error_${error.type}`]}</small>
</div>
<button data-component="button">
<div data-component="spinner">
<div>
{new Array(12).fill(0).map((i, k) => (
<div key={k} />
))}
</div>
</div>
{copy.button_continue}
</button>
<div style={{ padding: "2px 0" }} data-component="form-header">
<hr />
<span>
{copy.change_prompt}{" "}
<a data-component="link" href="change">
{copy.change}
</a>
</span>
</div>
</form>
</Layout>
)
return new Response(jsx.toString(), {
status: error ? 401 : 200,
headers: {
"Content-Type": "text/html",
},
})
},
register: async (_req, state, form, error): Promise<Response> => {
const emailError = ["invalid_email", "email_taken"].includes(
error?.type || "",
)
const passwordError = ["invalid_password", "password_mismatch"].includes(
error?.type || "",
)
//Just in case the server does it
const codeError = ["invalid_code"].includes(
error?.type || "",
)
const usernameError = ["invalid_username", "username_taken"].includes(
error?.type || "",
);
const jsx = (
<Layout page="password">
<div data-component="form-header">
<h1>Register</h1>
<span>
{copy.login_prompt}{" "}
<a data-component="link" href="authorize">
{copy.login}
</a>
</span>
<hr></hr>
</div>
<form data-component="form" method="post">
{/* <FormAlert message={error?.type && copy?.[`error_${error.type}`]} /> */}
{state.type === "start" && (
<>
<input type="hidden" name="action" value="register" />
<div
data-component="input-container"
>
<span>Email</span>
<div data-error={emailError} data-component="input-wrapper">
<span data-component="input-icon">
<svg width="24px" height="24px" stroke-width="1.5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="currentColor">
<path d="M7 9l5 3.5L17 9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
</path>
<path d="M2 17V7a2 2 0 012-2h16a2 2 0 012 2v10a2 2 0 01-2 2H4a2 2 0 01-2-2z" stroke="currentColor" stroke-width="1.5">
</path>
</svg>
</span>
<input
data-component="input"
autofocus={!error || emailError}
type="email"
name="email"
value={!emailError ? form?.get("email")?.toString() : ""}
required
placeholder={copy.input_email}
/>
</div>
<small>{error?.type && emailError && copy?.[`error_${error.type}`]}</small>
</div>
<div
data-component="input-container"
>
<span>Username</span>
<div data-error={usernameError} data-component="input-wrapper">
<span id="username-icon" data-component="input-icon">
<svg width="24px" height="24px" stroke-width="1.5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="currentColor"><path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M4.271 18.346S6.5 15.5 12 15.5s7.73 2.846 7.73 2.846M12 12a3 3 0 100-6 3 3 0 000 6z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg>
</span>
<input
id="username"
data-component="input"
autofocus={usernameError}
type="text"
name="username"
placeholder={copy.input_username}
required
value={
!usernameError ? form?.get("username")?.toString() : ""
}
/>
</div>
<small>{error?.type && usernameError && copy?.[`error_${error.type}`]}</small>
</div>
<div
data-component="input-container"
>
<span>Password</span>
<div data-error={passwordError} data-component="input-wrapper">
<span data-component="input-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" color="currentColor"><path d="M12 16.5v-2m-7.732 4.345c.225 1.67 1.608 2.979 3.292 3.056c1.416.065 2.855.099 4.44.099s3.024-.034 4.44-.1c1.684-.076 3.067-1.385 3.292-3.055c.147-1.09.268-2.207.268-3.345s-.121-2.255-.268-3.345c-.225-1.67-1.608-2.979-3.292-3.056A95 95 0 0 0 12 9c-1.585 0-3.024.034-4.44.1c-1.684.076-3.067 1.385-3.292 3.055C4.12 13.245 4 14.362 4 15.5s.121 2.255.268 3.345" /><path d="M7.5 9V6.5a4.5 4.5 0 0 1 9 0V9" /></g></svg>
</span>
<input
data-component="input"
id="password"
autofocus={passwordError}
type="password"
name="password"
placeholder={copy.input_password}
required
value={
!passwordError ? form?.get("password")?.toString() : ""
}
autoComplete="new-password"
/>
</div>
<small>{error?.type && passwordError && copy?.[`error_${error.type}`]}</small>
</div>
<button data-component="button">
<div data-component="spinner">
<div>
{new Array(12).fill(0).map((i, k) => (
<div key={k} />
))}
</div>
</div>
{copy.button_continue}
</button>
</>
)}
{state.type === "code" && (
<>
<input type="hidden" name="action" value="verify" />
<div
data-component="input-container"
>
<span>Code</span>
<div data-error={codeError} data-component="input-wrapper">
<span data-component="input-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" stroke-width="1.5" viewBox="0 0 24 24"><path fill="currentColor" fill-rule="evenodd" d="M2.43 8.25a1 1 0 0 1 1-1h.952c1.063 0 1.952.853 1.952 1.938v6.562a1 1 0 1 1-2 0v-6.5H3.43a1 1 0 0 1-1-1m5.714 0a1 1 0 0 1 1-1h2.857c1.064 0 1.953.853 1.953 1.938v1.874A1.945 1.945 0 0 1 12 13h-1.857v1.75h2.81a1 1 0 1 1 0 2h-2.858a1.945 1.945 0 0 1-1.952-1.937v-1.876c0-1.084.889-1.937 1.952-1.937h1.858V9.25h-2.81a1 1 0 0 1-1-1m7.619 0a1 1 0 0 1 1-1h2.857c1.063 0 1.953.853 1.953 1.938v5.624a1.945 1.945 0 0 1-1.953 1.938h-2.857a1 1 0 1 1 0-2h2.81V13h-2.81a1 1 0 1 1 0-2h2.81V9.25h-2.81a1 1 0 0 1-1-1" clip-rule="evenodd" /></svg>
</span>
<input
data-component="input"
autofocus
name="code"
minLength={6}
maxLength={6}
required
placeholder={copy.input_code}
autoComplete="one-time-code"
/>
</div>
<small>{error?.type && codeError && copy?.[`error_${error.type}`]}</small>
</div>
<button data-component="button">
<div data-component="spinner">
<div>
{new Array(12).fill(0).map((i, k) => (
<div key={k} />
))}
</div>
</div>
{copy.button_continue}
</button>
</>
)}
</form>
</Layout>
) as string
return new Response(jsx.toString(), {
headers: {
"Content-Type": "text/html",
},
})
},
change: async (_req, state, form, error): Promise<Response> => {
const passwordError = ["invalid_password", "password_mismatch"].includes(
error?.type || "",
)
const emailError = ["invalid_email", "email_taken"].includes(
error?.type || "",
)
const codeError = ["invalid_code"].includes(
error?.type || "",
)
const jsx = (
<Layout page="password">
<div data-component="form-header">
<h1>Forgot Password</h1>
{state.type != "update" && (
<span>
Suddenly had an epiphany?{" "}
<a data-component="link" href="authorize">
{copy.login}
</a>
</span>
)}
<hr />
</div>
<form data-component="form" method="post" replace>
{/* <FormAlert message={error?.type && copy?.[`error_${error.type}`]} /> */}
{state.type === "start" && (
<>
<input type="hidden" name="action" value="code" />
<div
data-component="input-container"
>
<span>Email</span>
<div data-error={emailError} data-component="input-wrapper">
<span data-component="input-icon">
<svg width="24px" height="24px" stroke-width="1.5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="currentColor">
<path d="M7 9l5 3.5L17 9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
</path>
<path d="M2 17V7a2 2 0 012-2h16a2 2 0 012 2v10a2 2 0 01-2 2H4a2 2 0 01-2-2z" stroke="currentColor" stroke-width="1.5">
</path>
</svg>
</span>
<input
data-component="input"
autofocus
type="email"
name="email"
required
value={form?.get("email")?.toString()}
placeholder={copy.input_email}
/>
</div>
<small>{error?.type && emailError && copy?.[`error_${error.type}`]}</small>
</div>
</>
)}
{state.type === "code" && (
<>
<input type="hidden" name="action" value="verify" />
<div
data-component="input-container"
>
<span>Code</span>
<div data-error={codeError} data-component="input-wrapper">
<span data-component="input-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" stroke-width="1.5" viewBox="0 0 24 24"><path fill="currentColor" fill-rule="evenodd" d="M2.43 8.25a1 1 0 0 1 1-1h.952c1.063 0 1.952.853 1.952 1.938v6.562a1 1 0 1 1-2 0v-6.5H3.43a1 1 0 0 1-1-1m5.714 0a1 1 0 0 1 1-1h2.857c1.064 0 1.953.853 1.953 1.938v1.874A1.945 1.945 0 0 1 12 13h-1.857v1.75h2.81a1 1 0 1 1 0 2h-2.858a1.945 1.945 0 0 1-1.952-1.937v-1.876c0-1.084.889-1.937 1.952-1.937h1.858V9.25h-2.81a1 1 0 0 1-1-1m7.619 0a1 1 0 0 1 1-1h2.857c1.063 0 1.953.853 1.953 1.938v5.624a1.945 1.945 0 0 1-1.953 1.938h-2.857a1 1 0 1 1 0-2h2.81V13h-2.81a1 1 0 1 1 0-2h2.81V9.25h-2.81a1 1 0 0 1-1-1" clip-rule="evenodd" /></svg>
</span>
<input
data-component="input"
autofocus
name="code"
minLength={6}
maxLength={6}
required
placeholder={copy.input_code}
autoComplete="one-time-code"
/>
</div>
<small>{error?.type && codeError && copy?.[`error_${error.type}`]}</small>
</div>
</>
)}
{state.type === "update" && (
<>
<input type="hidden" name="action" value="update" />
<div
data-component="input-container"
>
<span>Password</span>
<div data-error={passwordError} data-component="input-wrapper">
<span data-component="input-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" color="currentColor"><path d="M12 16.5v-2m-7.732 4.345c.225 1.67 1.608 2.979 3.292 3.056c1.416.065 2.855.099 4.44.099s3.024-.034 4.44-.1c1.684-.076 3.067-1.385 3.292-3.055c.147-1.09.268-2.207.268-3.345s-.121-2.255-.268-3.345c-.225-1.67-1.608-2.979-3.292-3.056A95 95 0 0 0 12 9c-1.585 0-3.024.034-4.44.1c-1.684.076-3.067 1.385-3.292 3.055C4.12 13.245 4 14.362 4 15.5s.121 2.255.268 3.345" /><path d="M7.5 9V6.5a4.5 4.5 0 0 1 9 0V9" /></g></svg>
</span>
<input
data-component="input"
autofocus
type="password"
name="password"
placeholder={copy.input_password}
required
value={
!passwordError ? form?.get("password")?.toString() : ""
}
autoComplete="new-password"
/>
</div>
<small>{error?.type && passwordError && copy?.[`error_${error.type}`]}</small>
</div>
<div
data-component="input-container"
>
<span>Confirm Password</span>
<div data-error={passwordError} data-component="input-wrapper">
<span data-component="input-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" color="currentColor"><path d="M12 16.5v-2m-7.732 4.345c.225 1.67 1.608 2.979 3.292 3.056c1.416.065 2.855.099 4.44.099s3.024-.034 4.44-.1c1.684-.076 3.067-1.385 3.292-3.055c.147-1.09.268-2.207.268-3.345s-.121-2.255-.268-3.345c-.225-1.67-1.608-2.979-3.292-3.056A95 95 0 0 0 12 9c-1.585 0-3.024.034-4.44.1c-1.684.076-3.067 1.385-3.292 3.055C4.12 13.245 4 14.362 4 15.5s.121 2.255.268 3.345" /><path d="M7.5 9V6.5a4.5 4.5 0 0 1 9 0V9" /></g></svg>
</span>
<input
data-component="input"
type="password"
name="repeat"
required
value={
!passwordError ? form?.get("password")?.toString() : ""
}
placeholder={copy.input_repeat}
autoComplete="new-password"
/>
</div>
<small>{error?.type && passwordError && copy?.[`error_${error.type}`]}</small>
</div>
</>
)}
<button data-component="button">
<div data-component="spinner">
<div>
{new Array(12).fill(0).map((i, k) => (
<div key={k} />
))}
</div>
</div>
{copy.button_continue}
</button>
</form>
{state.type === "code" && (
<form method="post">
<input type="hidden" name="action" value="code" />
<input type="hidden" name="email" value={state.email} />
</form>
)}
</Layout>
)
return new Response(jsx.toString(), {
status: error ? 400 : 200,
headers: {
"Content-Type": "text/html",
},
})
},
} satisfies PasswordConfig
}