Compare commits

...

2 Commits

Author SHA1 Message Date
c445dc13e8 login improvements
Some checks failed
ci / docker_image (push) Failing after 2m3s
ci / deploy (push) Has been skipped
2026-01-24 20:55:35 -06:00
be7b71fce4 better server build 2026-01-24 20:54:17 -06:00
10 changed files with 863 additions and 60 deletions

778
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -51,7 +51,9 @@
}, },
"dependencies": { "dependencies": {
"bcrypt": "^6.0.0", "bcrypt": "^6.0.0",
"desm": "^1.3.1",
"dotenv": "^17.2.3", "dotenv": "^17.2.3",
"express": "^5.2.1",
"jsonwebtoken": "^9.0.3", "jsonwebtoken": "^9.0.3",
"postgres": "^3.4.7", "postgres": "^3.4.7",
"svelte-preprocess": "^6.0.3" "svelte-preprocess": "^6.0.3"

18
server.js Normal file
View File

@ -0,0 +1,18 @@
import express from 'express';
import { handler } from './build/handler.js';
import { join } from 'desm';
const assetsPath = join(import.meta.url, 'uploads');
const app = express();
// Serve static files dynamically from the "static" folder (e.g., for uploads)
app.use('/uploads', express.static(assetsPath));
// Serve the built SvelteKit app
app.use(handler);
// Start the server
app.listen(8080, () => {
console.log('Server running on port 8080');
});

View File

@ -3,24 +3,20 @@
FieldGroup, FieldGroup,
Field, Field,
FieldLabel, FieldLabel,
FieldDescription, FieldDescription
FieldSeparator, } from '$lib/components/ui/field/index.js';
} from "$lib/components/ui/field/index.js"; import { Input } from '$lib/components/ui/input/index.js';
import { Input } from "$lib/components/ui/input/index.js"; import { Button } from '$lib/components/ui/button/index.js';
import { Button } from "$lib/components/ui/button/index.js"; import { cn, type WithElementRef } from '$lib/utils.js';
import { cn, type WithElementRef } from "$lib/utils.js"; import type { HTMLFormAttributes } from 'svelte/elements';
import type { HTMLFormAttributes } from "svelte/elements";
let { let {
ref = $bindable(null),
class: className, class: className,
...restProps ...restProps
}: WithElementRef<HTMLFormAttributes> = $props(); }: WithElementRef<HTMLFormAttributes> = $props();
const id = $props.id();
</script> </script>
<form class={cn("flex flex-col gap-6", className)} bind:this={ref} {...restProps}> <form class={cn("flex flex-col gap-6", className)} {...restProps}>
<FieldGroup> <FieldGroup>
<div class="flex flex-col items-center gap-1 text-center"> <div class="flex flex-col items-center gap-1 text-center">
<h1 class="text-2xl font-bold">Login to your account</h1> <h1 class="text-2xl font-bold">Login to your account</h1>
@ -29,35 +25,25 @@
</p> </p>
</div> </div>
<Field> <Field>
<FieldLabel for="email-{id}">Email</FieldLabel> <FieldLabel for="email">Email</FieldLabel>
<Input id="email-{id}" type="email" placeholder="m@example.com" required /> <Input id="email" type="email" placeholder="m@example.com" required />
</Field> </Field>
<Field> <Field>
<div class="flex items-center"> <div class="flex items-center">
<FieldLabel for="password-{id}">Password</FieldLabel> <FieldLabel for="password">Password</FieldLabel>
<a href="##" class="ms-auto text-sm underline-offset-4 hover:underline"> <a href="##" class="ms-auto text-sm underline-offset-4 hover:underline">
Forgot your password? Forgot your password?
</a> </a>
</div> </div>
<Input id="password-{id}" type="password" required /> <Input id="password" type="password" required />
</Field> </Field>
<Field> <Field>
<Button type="submit">Login</Button> <Button type="submit">Login</Button>
</Field> </Field>
<FieldSeparator>Or continue with</FieldSeparator>
<Field> <Field>
<Button variant="outline" type="button">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
fill="currentColor"
/>
</svg>
Login with GitHub
</Button>
<FieldDescription class="text-center"> <FieldDescription class="text-center">
Don't have an account? Don't have an account?
<a href="##" class="underline underline-offset-4">Sign up</a> <a href="/signup" class="underline underline-offset-4">Sign up</a>
</FieldDescription> </FieldDescription>
</Field> </Field>
</FieldGroup> </FieldGroup>

View File

@ -0,0 +1,26 @@
<script>
// Define props for size and color, with default values
export let size = 24;
export let color = 'currentColor';
</script>
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
viewBox="0 0 24 19"
fill={color}
stroke-width="0"
stroke-linecap="round"
stroke-linejoin="round"
{...$$restProps}
>
<path
d="m12.005 0.1012c-0.33679 0-0.56131 0.38889-0.56131 0.38889l-1.2349 2.1389 4.8113 8.3333c1.1806-0.26901 1.8534 1.2973 0.84835 1.9695-1.0051 0.6722-2.1957-0.54773-1.4963-1.5362l-4.6124-7.9889-8.4197 14.583c-0.14969 0.25926-0.16839 0.48611-0.056127 0.68056 0.11226 0.19445 0.31808 0.29167 0.61745 0.29167h2.4698l4.8113-8.3333c-0.82328-0.88794 0.19678-2.2537 1.2815-1.7194 1.0847 0.53431 0.62352 2.1754-0.58222 2.0639l-4.6124 7.9889h16.839s0.44905 0 0.61744-0.29167c0.16838-0.29167-0.05613-0.68056-0.05613-0.68056l-1.2349-2.1389h-9.6225c-0.35734 1.157-2.0502 0.95647-2.1298-0.25005-0.07962-1.2065 1.5722-1.6277 2.0785-0.52773h9.2248l-8.4197-14.583s-0.22452-0.38889-0.56132-0.38889" />
</svg>
<style>
/* Optional: Add CSS for styling, if needed */
svg {
display: block;
}
</style>

View File

@ -1,9 +1,9 @@
<script lang="ts"> <script lang="ts">
import { cn } from "$lib/utils.js"; import { cn } from '$lib/utils.js';
import { Button } from "$lib/components/ui/button/index.js"; import { Button } from '$lib/components/ui/button/index.js';
import * as Field from "$lib/components/ui/field/index.js"; import * as Field from '$lib/components/ui/field/index.js';
import { Input } from "$lib/components/ui/input/index.js"; import { Input } from '$lib/components/ui/input/index.js';
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
let { class: className, ...restProps }: HTMLAttributes<HTMLFormElement> = $props(); let { class: className, ...restProps }: HTMLAttributes<HTMLFormElement> = $props();
</script> </script>
@ -40,19 +40,9 @@
<Field.Field> <Field.Field>
<Button type="submit">Create Account</Button> <Button type="submit">Create Account</Button>
</Field.Field> </Field.Field>
<Field.Separator>Or continue with</Field.Separator>
<Field.Field> <Field.Field>
<Button variant="outline" type="button">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
fill="currentColor"
/>
</svg>
Sign up with GitHub
</Button>
<Field.Description class="px-6 text-center"> <Field.Description class="px-6 text-center">
Already have an account? <a href="#/">Sign in</a> Already have an account? <a href="/login">Sign in</a>
</Field.Description> </Field.Description>
</Field.Field> </Field.Field>
</Field.Group> </Field.Group>

View File

@ -1,18 +1,20 @@
<script lang="ts"> <script lang="ts">
import GalleryVerticalEndIcon from '@lucide/svelte/icons/gallery-vertical-end';
import LoginForm from '$lib/components/login-form.svelte'; import LoginForm from '$lib/components/login-form.svelte';
import MdevTriangle from '$lib/components/mdev-triangle.svelte';
</script> </script>
<div class="grid min-h-svh lg:grid-cols-2"> <div class="grid min-h-svh lg:grid-cols-2 w-screen">
<div class="flex flex-col gap-4 p-6 md:p-10"> <div class="flex flex-col gap-4 p-6 md:p-10">
<div class="flex justify-center gap-2 md:justify-start"> <div class="flex justify-center gap-2 md:justify-start">
<a href="##" class="flex items-center gap-2 font-medium"> <a href="/" class="flex items-center gap-2 font-medium">
<div <div
class="bg-primary text-primary-foreground flex size-6 items-center justify-center rounded-md" class="bg-primary text-primary-foreground flex size-8 items-center justify-center rounded-md"
> >
<GalleryVerticalEndIcon class="size-4" />
<MdevTriangle />
</div> </div>
Acme Inc. MarinoDev
</a> </a>
</div> </div>
<div class="flex flex-1 items-center justify-center"> <div class="flex flex-1 items-center justify-center">
@ -23,8 +25,8 @@
</div> </div>
<div class="bg-muted relative hidden lg:block"> <div class="bg-muted relative hidden lg:block">
<img <img
src="/placeholder.svg" src="login-hero.png"
alt="placeholder" alt=""
class="absolute inset-0 h-full w-full object-cover dark:brightness-[0.2] dark:grayscale" class="absolute inset-0 h-full w-full object-cover dark:brightness-[0.2] dark:grayscale"
/> />
</div> </div>

View File

@ -1,16 +1,16 @@
<script> <script lang="ts">
import GalleryVerticalEndIcon from "@lucide/svelte/icons/gallery-vertical-end"; import SignupForm from '$lib/components/signup-form.svelte';
import SignupForm from "$lib/components/signup-form.svelte"; import MdevTriangle from '$lib/components/mdev-triangle.svelte';
</script> </script>
<div class="grid min-h-svh lg:grid-cols-2"> <div class="grid min-h-svh lg:grid-cols-2 w-screen">
<div class="flex flex-col gap-4 p-6 md:p-10"> <div class="flex flex-col gap-4 p-6 md:p-10">
<div class="flex justify-center gap-2 md:justify-start"> <div class="flex justify-center gap-2 md:justify-start">
<a href="#/" class="flex items-center gap-2 font-medium"> <a href="/" class="flex items-center gap-2 font-medium">
<div <div
class="bg-primary text-primary-foreground flex size-6 items-center justify-center rounded-md" class="bg-primary text-primary-foreground flex size-8 items-center justify-center rounded-md"
> >
<GalleryVerticalEndIcon class="size-4" /> <MdevTriangle />
</div> </div>
Acme Inc. Acme Inc.
</a> </a>

BIN
static/login-hero.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 677 KiB

7
static/triangle-bw.svg Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24mm" height="19mm" version="1.1" viewBox="0 0 24 19" xml:space="preserve"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><script xlink:href="/home/mitchell/inkscape/triangle_logo.js"/>
<g><rect x="-.033073" y=".033073" width="24.015" height="18.955" fill="#00000000" stroke-width=".0"/>
<path d="m12.005 0.1012c-0.33679 0-0.56131 0.38889-0.56131 0.38889l-1.2349 2.1389 4.8113 8.3333c1.1806-0.26901 1.8534 1.2973 0.84835 1.9695-1.0051 0.6722-2.1957-0.54773-1.4963-1.5362l-4.6124-7.9889-8.4197 14.583c-0.14969 0.25926-0.16839 0.48611-0.056127 0.68056 0.11226 0.19445 0.31808 0.29167 0.61745 0.29167h2.4698l4.8113-8.3333c-0.82328-0.88794 0.19678-2.2537 1.2815-1.7194 1.0847 0.53431 0.62352 2.1754-0.58222 2.0639l-4.6124 7.9889h16.839s0.44905 0 0.61744-0.29167c0.16838-0.29167-0.05613-0.68056-0.05613-0.68056l-1.2349-2.1389h-9.6225c-0.35734 1.157-2.0502 0.95647-2.1298-0.25005-0.07962-1.2065 1.5722-1.6277 2.0785-0.52773h9.2248l-8.4197-14.583s-0.22452-0.38889-0.56132-0.38889"
fill="currentColor"/></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB