Browse Source

minor visual improvements

Milo Schwartz 6 months ago
parent
commit
0e99e2b62b

+ 2 - 2
README.md

@@ -4,7 +4,7 @@ Pangolin is a self-hosted tunneled reverse proxy management server with identity
 
 ### Installation and Documentation
 
--   [Installation Instructions](https://docs.fossorial.io)
+-   [Installation Instructions](https://docs.fossorial.io/Getting%20Started/quick-install)
 -   [Full Documentation](https://docs.fossorial.io)
 
 ## Preview
@@ -112,7 +112,7 @@ Pangolin was inspired by several existing projects and concepts:
 -   **Cloudflare Tunnels**:  
     A similar approach to proxying private resources securely, but Pangolin is a self-hosted alternative, giving you full control over your infrastructure.
 
--   **Authentic and Authelia**:  
+-   **Authentik and Authelia**:  
     These projects inspired Pangolin’s centralized authentication system for proxies, enabling robust user and role management.
 
 ## Licensing

+ 6 - 5
config.example.yml

@@ -1,6 +1,6 @@
 app:
-    base_url: https://fossorial.io
-    log_level: debug
+    base_url: https://proxy.example.com
+    log_level: info
     save_logs: false
 
 server:
@@ -20,7 +20,7 @@ traefik:
 
 gerbil:
     start_port: 51820
-    base_endpoint: fossorial.io
+    base_endpoint: proxy.example.com
     use_subdomain: false
     block_size: 16
     subnet_group: 10.0.0.0/8
@@ -33,9 +33,9 @@ rate_limits:
 email:
     smtp_host: host.hoster.net
     smtp_port: 587
-    smtp_user: no-reply@example.io
+    smtp_user: no-reply@example.com
     smtp_pass: aaaaaaaaaaaaaaaaaa
-    no_reply: no-reply@example.io
+    no_reply: no-reply@example.com
 
 users:
     server_admin:
@@ -46,3 +46,4 @@ flags:
     require_email_verification: true
     disable_signup_without_invite: true
     disable_user_create_org: true
+

+ 7 - 6
server/lib/config.ts

@@ -5,6 +5,7 @@ import { z } from "zod";
 import { fromError } from "zod-validation-error";
 import { __DIRNAME, APP_PATH } from "@server/lib/consts";
 import { loadAppVersion } from "@server/lib/loadAppVersion";
+import { passwordSchema } from "@server/auth/passwordSchema";
 
 const portSchema = z.number().positive().gt(0).lte(65535);
 
@@ -53,17 +54,17 @@ const environmentSchema = z.object({
     }),
     email: z
         .object({
-            smtp_host: z.string().optional(),
-            smtp_port: portSchema.optional(),
-            smtp_user: z.string().optional(),
-            smtp_pass: z.string().optional(),
-            no_reply: z.string().email().optional()
+            smtp_host: z.string(),
+            smtp_port: portSchema,
+            smtp_user: z.string(),
+            smtp_pass: z.string(),
+            no_reply: z.string().email(),
         })
         .optional(),
     users: z.object({
         server_admin: z.object({
             email: z.string().email(),
-            password: z.string()
+            password: passwordSchema
         })
     }),
     flags: z

+ 1 - 1
src/app/[orgId]/settings/access/roles/DeleteRoleForm.tsx

@@ -158,7 +158,7 @@ export default function DeleteRoleForm({
                         </CredenzaDescription>
                     </CredenzaHeader>
                     <CredenzaBody>
-                        <div className="space-y-8">
+                        <div className="space-y-4">
                             <div className="space-y-4">
                                 <p>
                                     You're about to delete the{" "}

+ 1 - 1
src/app/[orgId]/settings/access/users/InviteUserForm.tsx

@@ -180,7 +180,7 @@ export default function InviteUserForm({ open, setOpen }: InviteUserFormProps) {
                         </CredenzaDescription>
                     </CredenzaHeader>
                     <CredenzaBody>
-                        <div className="space-y-8">
+                        <div className="space-y-4">
                             {!inviteLink && (
                                 <Form {...form}>
                                     <form

+ 1 - 1
src/app/[orgId]/settings/general/page.tsx

@@ -84,7 +84,7 @@ export default function GeneralPage() {
             const res = await api.get<AxiosResponse<ListOrgsResponse>>(
                 `/orgs`
             );
-            
+
             if (res.status === 200) {
                 if (res.data.data.orgs.length > 0) {
                     const orgId = res.data.data.orgs[0].orgId;

+ 232 - 213
src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx

@@ -6,7 +6,7 @@ import { useToast } from "@app/hooks/useToast";
 import { useOrgContext } from "@app/hooks/useOrgContext";
 import { useResourceContext } from "@app/hooks/useResourceContext";
 import { AxiosResponse } from "axios";
-import { formatAxiosError } from "@app/lib/api";;
+import { formatAxiosError } from "@app/lib/api";
 import {
     GetResourceAuthInfoResponse,
     GetResourceWhitelistResponse,
@@ -383,7 +383,7 @@ export default function ResourceAuthenticationPage() {
             )}
 
             <div className="space-y-12">
-                <section className="space-y-8 lg:max-w-2xl">
+                <section className="space-y-4 lg:max-w-2xl">
                     <SettingsSectionTitle
                         title="Users & Roles"
                         description="Configure which users and roles can visit this resource"
@@ -397,9 +397,7 @@ export default function ResourceAuthenticationPage() {
                                 defaultChecked={resource.sso}
                                 onCheckedChange={(val) => setSsoEnabled(val)}
                             />
-                            <Label htmlFor="sso-toggle">
-                                Use Platform SSO
-                            </Label>
+                            <Label htmlFor="sso-toggle">Use Platform SSO</Label>
                         </div>
                         <span className="text-muted-foreground text-sm">
                             Existing users will only have to login once for all
@@ -414,120 +412,134 @@ export default function ResourceAuthenticationPage() {
                             )}
                             className="space-y-4"
                         >
-                            <FormField
-                                control={usersRolesForm.control}
-                                name="roles"
-                                render={({ field }) => (
-                                    <FormItem className="flex flex-col items-start">
-                                        <FormLabel>Roles</FormLabel>
-                                        <FormControl>
-                                            {/* @ts-ignore */}
-                                            <TagInput
-                                                {...field}
-                                                activeTagIndex={
-                                                    activeRolesTagIndex
-                                                }
-                                                setActiveTagIndex={
-                                                    setActiveRolesTagIndex
-                                                }
-                                                placeholder="Enter a role"
-                                                tags={
-                                                    usersRolesForm.getValues()
-                                                        .roles
-                                                }
-                                                setTags={(newRoles) => {
-                                                    usersRolesForm.setValue(
-                                                        "roles",
-                                                        newRoles as [
-                                                            Tag,
-                                                            ...Tag[]
-                                                        ]
-                                                    );
-                                                }}
-                                                enableAutocomplete={true}
-                                                autocompleteOptions={allRoles}
-                                                allowDuplicates={false}
-                                                restrictTagsToAutocompleteOptions={
-                                                    true
-                                                }
-                                                sortTags={true}
-                                                styleClasses={{
-                                                    tag: {
-                                                        body: "bg-muted hover:bg-accent text-foreground py-2 px-3 rounded-full"
-                                                    },
-                                                    input: "text-base md:text-sm border-none bg-transparent text-inherit placeholder:text-inherit shadow-none",
-                                                    inlineTagsContainer:
-                                                        "bg-transparent p-2"
-                                                }}
-                                            />
-                                        </FormControl>
-                                        <FormDescription>
-                                            These roles will be able to access
-                                            this resource. Admins can always
-                                            access this resource.
-                                        </FormDescription>
-                                        <FormMessage />
-                                    </FormItem>
-                                )}
-                            />
-                            <FormField
-                                control={usersRolesForm.control}
-                                name="users"
-                                render={({ field }) => (
-                                    <FormItem className="flex flex-col items-start">
-                                        <FormLabel>Users</FormLabel>
-                                        <FormControl>
-                                            {/* @ts-ignore */}
-                                            <TagInput
-                                                {...field}
-                                                activeTagIndex={
-                                                    activeUsersTagIndex
-                                                }
-                                                setActiveTagIndex={
-                                                    setActiveUsersTagIndex
-                                                }
-                                                placeholder="Enter a user"
-                                                tags={
-                                                    usersRolesForm.getValues()
-                                                        .users
-                                                }
-                                                setTags={(newUsers) => {
-                                                    usersRolesForm.setValue(
-                                                        "users",
-                                                        newUsers as [
-                                                            Tag,
-                                                            ...Tag[]
-                                                        ]
-                                                    );
-                                                }}
-                                                enableAutocomplete={true}
-                                                autocompleteOptions={allUsers}
-                                                allowDuplicates={false}
-                                                restrictTagsToAutocompleteOptions={
-                                                    true
-                                                }
-                                                sortTags={true}
-                                                styleClasses={{
-                                                    tag: {
-                                                        body: "bg-muted hover:bg-accent text-foreground py-2 px-3 rounded-full"
-                                                    },
-                                                    input: "text-base md:text-sm border-none bg-transparent text-inherit placeholder:text-inherit shadow-none",
-                                                    inlineTagsContainer:
-                                                        "bg-transparent p-2"
-                                                }}
-                                            />
-                                        </FormControl>
-                                        <FormDescription>
-                                            Users added here will be able to
-                                            access this resource. A user will
-                                            always have access to a resource if
-                                            they have a role that has access to
-                                            it.
-                                        </FormDescription>
-                                        <FormMessage />
-                                    </FormItem>
-                                )}
-                            />
+                            {ssoEnabled && (
+                                <>
+                                    <FormField
+                                        control={usersRolesForm.control}
+                                        name="roles"
+                                        render={({ field }) => (
+                                            <FormItem className="flex flex-col items-start">
+                                                <FormLabel>Roles</FormLabel>
+                                                <FormControl>
+                                                    {/* @ts-ignore */}
+                                                    <TagInput
+                                                        {...field}
+                                                        activeTagIndex={
+                                                            activeRolesTagIndex
+                                                        }
+                                                        setActiveTagIndex={
+                                                            setActiveRolesTagIndex
+                                                        }
+                                                        placeholder="Enter a role"
+                                                        tags={
+                                                            usersRolesForm.getValues()
+                                                                .roles
+                                                        }
+                                                        setTags={(newRoles) => {
+                                                            usersRolesForm.setValue(
+                                                                "roles",
+                                                                newRoles as [
+                                                                    Tag,
+                                                                    ...Tag[]
+                                                                ]
+                                                            );
+                                                        }}
+                                                        enableAutocomplete={
+                                                            true
+                                                        }
+                                                        autocompleteOptions={
+                                                            allRoles
+                                                        }
+                                                        allowDuplicates={false}
+                                                        restrictTagsToAutocompleteOptions={
+                                                            true
+                                                        }
+                                                        sortTags={true}
+                                                        styleClasses={{
+                                                            tag: {
+                                                                body: "bg-muted hover:bg-accent text-foreground py-2 px-3 rounded-full"
+                                                            },
+                                                            input: "text-base md:text-sm border-none bg-transparent text-inherit placeholder:text-inherit shadow-none",
+                                                            inlineTagsContainer:
+                                                                "bg-transparent p-2"
+                                                        }}
+                                                    />
+                                                </FormControl>
+                                                <FormDescription>
+                                                    These roles will be able to
+                                                    access this resource. Admins
+                                                    can always access this
+                                                    resource.
+                                                </FormDescription>
+                                                <FormMessage />
+                                            </FormItem>
+                                        )}
+                                    />
+                                    <FormField
+                                        control={usersRolesForm.control}
+                                        name="users"
+                                        render={({ field }) => (
+                                            <FormItem className="flex flex-col items-start">
+                                                <FormLabel>Users</FormLabel>
+                                                <FormControl>
+                                                    {/* @ts-ignore */}
+                                                    <TagInput
+                                                        {...field}
+                                                        activeTagIndex={
+                                                            activeUsersTagIndex
+                                                        }
+                                                        setActiveTagIndex={
+                                                            setActiveUsersTagIndex
+                                                        }
+                                                        placeholder="Enter a user"
+                                                        tags={
+                                                            usersRolesForm.getValues()
+                                                                .users
+                                                        }
+                                                        setTags={(newUsers) => {
+                                                            usersRolesForm.setValue(
+                                                                "users",
+                                                                newUsers as [
+                                                                    Tag,
+                                                                    ...Tag[]
+                                                                ]
+                                                            );
+                                                        }}
+                                                        enableAutocomplete={
+                                                            true
+                                                        }
+                                                        autocompleteOptions={
+                                                            allUsers
+                                                        }
+                                                        allowDuplicates={false}
+                                                        restrictTagsToAutocompleteOptions={
+                                                            true
+                                                        }
+                                                        sortTags={true}
+                                                        styleClasses={{
+                                                            tag: {
+                                                                body: "bg-muted hover:bg-accent text-foreground py-2 px-3 rounded-full"
+                                                            },
+                                                            input: "text-base md:text-sm border-none bg-transparent text-inherit placeholder:text-inherit shadow-none",
+                                                            inlineTagsContainer:
+                                                                "bg-transparent p-2"
+                                                        }}
+                                                    />
+                                                </FormControl>
+                                                <FormDescription>
+                                                    Users added here will be
+                                                    able to access this
+                                                    resource. A user will always
+                                                    have access to a resource if
+                                                    they have a role that has
+                                                    access to it.
+                                                </FormDescription>
+                                                <FormMessage />
+                                            </FormItem>
+                                        )}
+                                    />
+                                </>
+                            )}
                             <Button
                                 type="submit"
                                 loading={loadingSaveUsersRoles}
@@ -541,7 +553,7 @@ export default function ResourceAuthenticationPage() {
 
                 <Separator />
 
-                <section className="space-y-8 lg:max-w-2xl">
+                <section className="space-y-4 lg:max-w-2xl">
                     <SettingsSectionTitle
                         title="Authentication Methods"
                         description="Allow access to the resource via additional auth methods"
@@ -617,101 +629,108 @@ export default function ResourceAuthenticationPage() {
 
                 <Separator />
 
-                <section className="space-y-8 lg:max-w-2xl">
-                {env.EMAIL_ENABLED === "true" && (
-                    <>
-                        <div>
-                            <div className="flex items-center space-x-2 mb-2">
-                                <Switch
-                                    id="whitelist-toggle"
-                                    defaultChecked={
-                                        resource.emailWhitelistEnabled
-                                    }
-                                    onCheckedChange={(val) =>
-                                        setWhitelistEnabled(val)
-                                    }
-                                />
-                                <Label htmlFor="whitelist-toggle">
-                                    Email Whitelist
-                                </Label>
+                <section className="space-y-4 lg:max-w-2xl">
+                    {env.EMAIL_ENABLED === "true" && (
+                        <>
+                            <div>
+                                <div className="flex items-center space-x-2 mb-2">
+                                    <Switch
+                                        id="whitelist-toggle"
+                                        defaultChecked={
+                                            resource.emailWhitelistEnabled
+                                        }
+                                        onCheckedChange={(val) =>
+                                            setWhitelistEnabled(val)
+                                        }
+                                    />
+                                    <Label htmlFor="whitelist-toggle">
+                                        Email Whitelist
+                                    </Label>
+                                </div>
+                                <span className="text-muted-foreground text-sm">
+                                    Enable resource whitelist to require
+                                    email-based authentication (one-time
+                                    passwords) for resource access.
+                                </span>
                             </div>
-                            <span className="text-muted-foreground text-sm">
-                                Enable resource whitelist to require email-based
-                                authentication (one-time passwords) for resource
-                                access.
-                            </span>
-                        </div>
 
-                        {whitelistEnabled && (
-                            <Form {...whitelistForm}>
-                                <form className="space-y-4">
-                                    <FormField
-                                        control={whitelistForm.control}
-                                        name="emails"
-                                        render={({ field }) => (
-                                            <FormItem className="flex flex-col items-start">
-                                                <FormLabel>
-                                                    Whitelisted Emails
-                                                </FormLabel>
-                                                <FormControl>
-                                                    {/* @ts-ignore */}
-                                                    <TagInput
-                                                        {...field}
-                                                        activeTagIndex={
-                                                            activeEmailTagIndex
-                                                        }
-                                                        validateTag={(tag) => {
-                                                            return z
-                                                                .string()
-                                                                .email()
-                                                                .safeParse(tag)
-                                                                .success;
-                                                        }}
-                                                        setActiveTagIndex={
-                                                            setActiveEmailTagIndex
-                                                        }
-                                                        placeholder="Enter an email"
-                                                        tags={
-                                                            whitelistForm.getValues()
-                                                                .emails
-                                                        }
-                                                        setTags={(newRoles) => {
-                                                            whitelistForm.setValue(
-                                                                "emails",
-                                                                newRoles as [
-                                                                    Tag,
-                                                                    ...Tag[]
-                                                                ]
-                                                            );
-                                                        }}
-                                                        allowDuplicates={false}
-                                                        sortTags={true}
-                                                        styleClasses={{
-                                                            tag: {
-                                                                body: "bg-muted hover:bg-accent text-foreground py-2 px-3 rounded-full"
-                                                            },
-                                                            input: "text-base md:text-sm border-none bg-transparent text-inherit placeholder:text-inherit shadow-none",
-                                                            inlineTagsContainer:
-                                                                "bg-transparent p-2"
-                                                        }}
-                                                    />
-                                                </FormControl>
-                                            </FormItem>
-                                        )}
-                                    />
-                                </form>
-                            </Form>
-                        )}
-
-                        <Button
-                            loading={loadingSaveWhitelist}
-                            disabled={loadingSaveWhitelist}
-                            onClick={saveWhitelist}
-                        >
-                            Save Whitelist
-                        </Button>
-                    </>
-                )}
+                            {whitelistEnabled && (
+                                <Form {...whitelistForm}>
+                                    <form className="space-y-4">
+                                        <FormField
+                                            control={whitelistForm.control}
+                                            name="emails"
+                                            render={({ field }) => (
+                                                <FormItem className="flex flex-col items-start">
+                                                    <FormLabel>
+                                                        Whitelisted Emails
+                                                    </FormLabel>
+                                                    <FormControl>
+                                                        {/* @ts-ignore */}
+                                                        <TagInput
+                                                            {...field}
+                                                            activeTagIndex={
+                                                                activeEmailTagIndex
+                                                            }
+                                                            validateTag={(
+                                                                tag
+                                                            ) => {
+                                                                return z
+                                                                    .string()
+                                                                    .email()
+                                                                    .safeParse(
+                                                                        tag
+                                                                    ).success;
+                                                            }}
+                                                            setActiveTagIndex={
+                                                                setActiveEmailTagIndex
+                                                            }
+                                                            placeholder="Enter an email"
+                                                            tags={
+                                                                whitelistForm.getValues()
+                                                                    .emails
+                                                            }
+                                                            setTags={(
+                                                                newRoles
+                                                            ) => {
+                                                                whitelistForm.setValue(
+                                                                    "emails",
+                                                                    newRoles as [
+                                                                        Tag,
+                                                                        ...Tag[]
+                                                                    ]
+                                                                );
+                                                            }}
+                                                            allowDuplicates={
+                                                                false
+                                                            }
+                                                            sortTags={true}
+                                                            styleClasses={{
+                                                                tag: {
+                                                                    body: "bg-muted hover:bg-accent text-foreground py-2 px-3 rounded-full"
+                                                                },
+                                                                input: "text-base md:text-sm border-none bg-transparent text-inherit placeholder:text-inherit shadow-none",
+                                                                inlineTagsContainer:
+                                                                    "bg-transparent p-2"
+                                                            }}
+                                                        />
+                                                    </FormControl>
+                                                </FormItem>
+                                            )}
+                                        />
+                                    </form>
+                                </Form>
+                            )}
+
+                            <Button
+                                loading={loadingSaveWhitelist}
+                                disabled={loadingSaveWhitelist}
+                                onClick={saveWhitelist}
+                            >
+                                Save Whitelist
+                            </Button>
+                        </>
+                    )}
                 </section>
             </div>
         </>

+ 18 - 19
src/app/[orgId]/settings/resources/[resourceId]/connectivity/page.tsx

@@ -304,8 +304,8 @@ export default function ReverseProxyTargets(props: {
                         {row.original.method}
                     </SelectTrigger>
                     <SelectContent>
-                        <SelectItem value="http">HTTP</SelectItem>
-                        <SelectItem value="https">HTTPS</SelectItem>
+                        <SelectItem value="http">http</SelectItem>
+                        <SelectItem value="https">https</SelectItem>
                     </SelectContent>
                 </Select>
             )
@@ -412,7 +412,7 @@ export default function ReverseProxyTargets(props: {
     return (
         <>
             <div className="space-y-12">
-                <section className="space-y-8">
+                <section className="space-y-4">
                     <SettingsSectionTitle
                         title="SSL"
                         description="Setup SSL to secure your connections with LetsEncrypt certificates"
@@ -431,14 +431,14 @@ export default function ReverseProxyTargets(props: {
 
                 <hr />
 
-                <section className="space-y-8">
+                <section className="space-y-4">
                     <SettingsSectionTitle
                         title="Targets"
                         description="Setup targets to route traffic to your services"
                         size="1xl"
                     />
 
-                    <div className="space-y-8">
+                    <div className="space-y-4">
                         <Form {...addTargetForm}>
                             <form
                                 onSubmit={addTargetForm.handleSubmit(
@@ -470,18 +470,18 @@ export default function ReverseProxyTargets(props: {
                                                         </SelectTrigger>
                                                         <SelectContent>
                                                             <SelectItem value="http">
-                                                                HTTP
+                                                                http
                                                             </SelectItem>
                                                             <SelectItem value="https">
-                                                                HTTPS
+                                                                https
                                                             </SelectItem>
                                                         </SelectContent>
                                                     </Select>
                                                 </FormControl>
-                                                <FormDescription>
-                                                    Choose the method for how
-                                                    the target is accessed.
-                                                </FormDescription>
+                                                {/* <FormDescription> */}
+                                                {/*     Choose the method for how */}
+                                                {/*     the target is accessed. */}
+                                                {/* </FormDescription> */}
                                                 <FormMessage />
                                             </FormItem>
                                         )}
@@ -497,10 +497,9 @@ export default function ReverseProxyTargets(props: {
                                                 <FormControl>
                                                     <Input id="ip" {...field} />
                                                 </FormControl>
-                                                <FormDescription>
-                                                    Enter the IP address of the
-                                                    target.
-                                                </FormDescription>
+                                                {/* <FormDescription> */}
+                                                {/*     Use the IP of the resource on your private network if using Newt, or the peer IP if using raw WireGuard. */}
+                                                {/* </FormDescription> */}
                                                 <FormMessage />
                                             </FormItem>
                                         )}
@@ -519,10 +518,10 @@ export default function ReverseProxyTargets(props: {
                                                         required
                                                     />
                                                 </FormControl>
-                                                <FormDescription>
-                                                    Specify the port number for
-                                                    the target.
-                                                </FormDescription>
+                                                {/* <FormDescription> */}
+                                                {/*     Specify the port number for */}
+                                                {/*     the target. */}
+                                                {/* </FormDescription> */}
                                                 <FormMessage />
                                             </FormItem>
                                         )}

+ 1 - 1
src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx

@@ -125,7 +125,7 @@ export default function GeneralForm() {
     return (
         <>
             <div className="space-y-12 lg:max-w-2xl">
-                <section className="space-y-8">
+                <section className="space-y-4">
                     <SettingsSectionTitle
                         title="General Settings"
                         description="Configure the general settings for this resource"

+ 1 - 1
src/app/[orgId]/settings/sites/CreateSiteForm.tsx

@@ -199,7 +199,7 @@ PersistentKeepalive = 5`
     const newtConfig = `newt --id ${siteDefaults?.newtId} --secret ${siteDefaults?.newtSecret} --endpoint ${proto}//${siteDefaults?.endpoint}`;
 
     return (
-        <div className="space-y-8">
+        <div className="space-y-4">
             <Form {...form}>
                 <form
                     onSubmit={form.handleSubmit(onSubmit)}

+ 1 - 1
src/app/[orgId]/settings/sites/[niceId]/general/page.tsx

@@ -67,7 +67,7 @@ export default function GeneralPage() {
 
     return (
         <>
-            <div className="space-y-8 max-w-xl">
+            <div className="space-y-4 max-w-xl">
                 <SettingsSectionTitle
                     title="General Settings"
                     description="Configure the general settings for this site"

+ 1 - 1
src/app/auth/login/DashboardLoginForm.tsx

@@ -37,7 +37,7 @@ export default function DashboardLoginForm({
     return (
         <Card className="w-full max-w-md">
             <CardHeader>
-                <CardTitle>Login</CardTitle>
+                <CardTitle>Welcome to Pangolin</CardTitle>
                 <CardDescription>
                     Enter your credentials to access your dashboard
                 </CardDescription>

+ 2 - 2
src/app/auth/resource/[resourceId]/ResourceAuthPortal.tsx

@@ -401,7 +401,7 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) {
                                                     disabled={loadingLogin}
                                                 >
                                                     <LockIcon className="w-4 h-4 mr-2" />
-                                                    Login with PIN
+                                                    Log in with PIN
                                                 </Button>
                                             </form>
                                         </Form>
@@ -456,7 +456,7 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) {
                                                     disabled={loadingLogin}
                                                 >
                                                     <LockIcon className="w-4 h-4 mr-2" />
-                                                    Login with Password
+                                                    Log In with Password
                                                 </Button>
                                             </form>
                                         </Form>

+ 1 - 1
src/app/invite/InviteStatusCard.tsx

@@ -91,7 +91,7 @@ export default function InviteStatusCard({
             );
         } else if (type === "wrong_user") {
             return (
-                <Button onClick={goToLogin}>Login in as different user</Button>
+                <Button onClick={goToLogin}>Log in as different user</Button>
             );
         } else if (type === "user_does_not_exist") {
             return <Button onClick={goToSignup}>Create an account</Button>;

+ 4 - 9
src/app/layout.tsx

@@ -5,10 +5,6 @@ import { Toaster } from "@/components/ui/toaster";
 import { ThemeProvider } from "@app/providers/ThemeProvider";
 import EnvProvider from "@app/providers/EnvProvider";
 import { Separator } from "@app/components/ui/separator";
-import { cache } from "react";
-import { verifySession } from "@app/lib/auth/verifySession";
-import Header from "@app/components/Header";
-import UserProvider from "@app/providers/UserProvider";
 
 export const metadata: Metadata = {
     title: `Dashboard - Pangolin`,
@@ -26,7 +22,7 @@ export default async function RootLayout({
 
     return (
         <html suppressHydrationWarning>
-            <body className={`${font.className}`}>
+            <body className={`${font.className} min-h-screen flex flex-col`}>
                 <ThemeProvider
                     attribute="class"
                     defaultTheme="system"
@@ -34,23 +30,22 @@ export default async function RootLayout({
                     disableTransitionOnChange
                 >
                     <EnvProvider
-                        // it's import not to pass all of process.env here in case of secrets
-                        // select only the necessary ones
                         env={{
                             NEXT_PORT: process.env.NEXT_PORT as string,
                             SERVER_EXTERNAL_PORT: process.env
                                 .SERVER_EXTERNAL_PORT as string,
                             ENVIRONMENT: process.env.ENVIRONMENT as string,
                             EMAIL_ENABLED: process.env.EMAIL_ENABLED as string,
-                            // optional
                             DISABLE_USER_CREATE_ORG:
                                 process.env.DISABLE_USER_CREATE_ORG,
                             DISABLE_SIGNUP_WITHOUT_INVITE:
                                 process.env.DISABLE_SIGNUP_WITHOUT_INVITE
                         }}
                     >
-                        {children}
+                        {/* Main content */}
+                        <div className="flex-grow">{children}</div>
 
+                        {/* Footer */}
                         <footer className="w-full mt-12 py-3 mb-4">
                             <div className="container mx-auto flex flex-wrap justify-center items-center h-4 space-x-4 text-sm text-neutral-400 select-none">
                                 <div className="whitespace-nowrap">

+ 4 - 4
src/components/LoginForm.tsx

@@ -130,7 +130,7 @@ export default function LoginForm({ redirect, onLogin }: LoginFormProps) {
     }
 
     return (
-        <div className="space-y-8">
+        <div className="space-y-4">
             {!mfaRequested && (
                 <Form {...form}>
                     <form
@@ -179,7 +179,7 @@ export default function LoginForm({ redirect, onLogin }: LoginFormProps) {
                                     href={`/auth/reset-password${form.getValues().email ? `?email=${form.getValues().email}` : ""}`}
                                     className="text-sm text-muted-foreground"
                                 >
-                                    Forgot password?
+                                    Forgot your password?
                                 </Link>
                             </div>
                         </div>
@@ -279,7 +279,7 @@ export default function LoginForm({ redirect, onLogin }: LoginFormProps) {
                         disabled={loading}
                     >
                         <LockIcon className="w-4 h-4 mr-2" />
-                        Login
+                        Log In
                     </Button>
                 )}
 
@@ -293,7 +293,7 @@ export default function LoginForm({ redirect, onLogin }: LoginFormProps) {
                             mfaForm.reset();
                         }}
                     >
-                        Back to Login
+                        Back to Log In
                     </Button>
                 )}
             </div>

+ 2 - 2
src/components/ui/alert.tsx

@@ -10,9 +10,9 @@ const alertVariants = cva(
             variant: {
                 default: "bg-background text-foreground",
                 destructive:
-                    "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
+                    "border-destructive/50 bg-destructive/10 text-destructive dark:border-destructive [&>svg]:text-destructive",
                 success:
-                    "border-green-500/50 text-green-500 dark:border-success [&>svg]:text-green-500",
+                    "border-green-500/50 bg-green-500/10 text-green-500 dark:border-success [&>svg]:text-green-500",
             },
         },
         defaultVariants: {