1
0
mirror of https://github.com/pocket-id/pocket-id.git synced 2026-02-04 15:39:45 +00:00

feat: make home page URL configurable (#1215)

This commit is contained in:
Elias Schneider
2026-01-07 22:01:51 +01:00
committed by GitHub
parent 03f9be0d12
commit 0a94f0fd64
16 changed files with 63 additions and 12 deletions

View File

@@ -14,6 +14,7 @@ type AppConfigVariableDto struct {
type AppConfigUpdateDto struct { type AppConfigUpdateDto struct {
AppName string `json:"appName" binding:"required,min=1,max=30" unorm:"nfc"` AppName string `json:"appName" binding:"required,min=1,max=30" unorm:"nfc"`
SessionDuration string `json:"sessionDuration" binding:"required"` SessionDuration string `json:"sessionDuration" binding:"required"`
HomePageURL string `json:"homePageUrl" binding:"required"`
EmailsVerified string `json:"emailsVerified" binding:"required"` EmailsVerified string `json:"emailsVerified" binding:"required"`
DisableAnimations string `json:"disableAnimations" binding:"required"` DisableAnimations string `json:"disableAnimations" binding:"required"`
AllowOwnAccountEdit string `json:"allowOwnAccountEdit" binding:"required"` AllowOwnAccountEdit string `json:"allowOwnAccountEdit" binding:"required"`

View File

@@ -36,6 +36,7 @@ type AppConfig struct {
// General // General
AppName AppConfigVariable `key:"appName,public"` // Public AppName AppConfigVariable `key:"appName,public"` // Public
SessionDuration AppConfigVariable `key:"sessionDuration"` SessionDuration AppConfigVariable `key:"sessionDuration"`
HomePageURL AppConfigVariable `key:"homePageUrl,public"` // Public
EmailsVerified AppConfigVariable `key:"emailsVerified"` EmailsVerified AppConfigVariable `key:"emailsVerified"`
AccentColor AppConfigVariable `key:"accentColor,public"` // Public AccentColor AppConfigVariable `key:"accentColor,public"` // Public
DisableAnimations AppConfigVariable `key:"disableAnimations,public"` // Public DisableAnimations AppConfigVariable `key:"disableAnimations,public"` // Public

View File

@@ -61,6 +61,7 @@ func (s *AppConfigService) getDefaultDbConfig() *model.AppConfig {
// General // General
AppName: model.AppConfigVariable{Value: "Pocket ID"}, AppName: model.AppConfigVariable{Value: "Pocket ID"},
SessionDuration: model.AppConfigVariable{Value: "60"}, SessionDuration: model.AppConfigVariable{Value: "60"},
HomePageURL: model.AppConfigVariable{Value: "/settings/account"},
EmailsVerified: model.AppConfigVariable{Value: "false"}, EmailsVerified: model.AppConfigVariable{Value: "false"},
DisableAnimations: model.AppConfigVariable{Value: "false"}, DisableAnimations: model.AppConfigVariable{Value: "false"},
AllowOwnAccountEdit: model.AppConfigVariable{Value: "true"}, AllowOwnAccountEdit: model.AppConfigVariable{Value: "true"},

View File

@@ -505,5 +505,7 @@
"scopes": "Scopes", "scopes": "Scopes",
"issuer_url": "Issuer URL", "issuer_url": "Issuer URL",
"smtp_field_required_when_other_provided": "Required when any SMTP setting is provided", "smtp_field_required_when_other_provided": "Required when any SMTP setting is provided",
"smtp_field_required_when_email_enabled": "Required when email notifications are enabled" "smtp_field_required_when_email_enabled": "Required when email notifications are enabled",
"app_config_home_page": "Home Page",
"app_config_home_page_description": "The page users are redirected to after signing in."
} }

View File

@@ -28,7 +28,7 @@
<div class="flex h-16 items-center"> <div class="flex h-16 items-center">
{#if !isAuthPage} {#if !isAuthPage}
<a <a
href="/settings/account" href="/"
class="flex items-center gap-3 transition-opacity hover:opacity-80" class="flex items-center gap-3 transition-opacity hover:opacity-80"
> >
<Logo class="size-8" /> <Logo class="size-8" />

View File

@@ -1,5 +1,5 @@
import userStore from '$lib/stores/user-store'; import userStore from '$lib/stores/user-store';
import type { AllAppConfig, AppConfigRawResponse } from '$lib/types/application-configuration'; import type { AllAppConfig, AppConfigRawResponse } from '$lib/types/application-configuration.type';
import { import {
cachedApplicationLogo, cachedApplicationLogo,
cachedBackgroundImage, cachedBackgroundImage,

View File

@@ -1,5 +1,5 @@
import AppConfigService from '$lib/services/app-config-service'; import AppConfigService from '$lib/services/app-config-service';
import type { AppConfig } from '$lib/types/application-configuration'; import type { AppConfig } from '$lib/types/application-configuration.type';
import { applyAccentColor } from '$lib/utils/accent-color-util'; import { applyAccentColor } from '$lib/utils/accent-color-util';
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';

View File

@@ -2,6 +2,7 @@ import type { CustomClaim } from './custom-claim.type';
export type AppConfig = { export type AppConfig = {
appName: string; appName: string;
homePageUrl: string;
allowOwnAccountEdit: boolean; allowOwnAccountEdit: boolean;
allowUserSignups: 'disabled' | 'withToken' | 'open'; allowUserSignups: 'disabled' | 'withToken' | 'open';
emailOneTimeAccessAsUnauthenticatedEnabled: boolean; emailOneTimeAccessAsUnauthenticatedEnabled: boolean;

View File

@@ -1,5 +1,5 @@
import VersionService from '$lib/services/version-service'; import VersionService from '$lib/services/version-service';
import type { AppVersionInformation } from '$lib/types/application-configuration'; import type { AppVersionInformation } from '$lib/types/application-configuration.type';
import type { LayoutLoad } from './$types'; import type { LayoutLoad } from './$types';
export const load: LayoutLoad = async () => { export const load: LayoutLoad = async () => {

View File

@@ -1,6 +1,8 @@
import { redirect } from '@sveltejs/kit'; import { redirect } from '@sveltejs/kit';
import type { PageLoad } from './$types'; import type { PageLoad } from './$types';
import appConfig from '$lib/stores/application-configuration-store';
import { get } from 'svelte/store';
export const load: PageLoad = async () => { export const load: PageLoad = async () => {
throw redirect(307, '/settings/account'); throw redirect(307, get(appConfig).homePageUrl);
}; };

View File

@@ -4,7 +4,7 @@
import { m } from '$lib/paraglide/messages'; import { m } from '$lib/paraglide/messages';
import AppConfigService from '$lib/services/app-config-service'; import AppConfigService from '$lib/services/app-config-service';
import appConfigStore from '$lib/stores/application-configuration-store'; import appConfigStore from '$lib/stores/application-configuration-store';
import type { AllAppConfig } from '$lib/types/application-configuration'; import type { AllAppConfig } from '$lib/types/application-configuration.type';
import { axiosErrorToast } from '$lib/utils/error-util'; import { axiosErrorToast } from '$lib/utils/error-util';
import { import {
LucideImage, LucideImage,

View File

@@ -8,7 +8,7 @@
import { m } from '$lib/paraglide/messages'; import { m } from '$lib/paraglide/messages';
import AppConfigService from '$lib/services/app-config-service'; import AppConfigService from '$lib/services/app-config-service';
import appConfigStore from '$lib/stores/application-configuration-store'; import appConfigStore from '$lib/stores/application-configuration-store';
import type { AllAppConfig } from '$lib/types/application-configuration'; import type { AllAppConfig } from '$lib/types/application-configuration.type';
import { preventDefault } from '$lib/utils/event-util'; import { preventDefault } from '$lib/utils/event-util';
import { createForm } from '$lib/utils/form-util'; import { createForm } from '$lib/utils/form-util';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';

View File

@@ -3,9 +3,10 @@
import SwitchWithLabel from '$lib/components/form/switch-with-label.svelte'; import SwitchWithLabel from '$lib/components/form/switch-with-label.svelte';
import { Button } from '$lib/components/ui/button'; import { Button } from '$lib/components/ui/button';
import * as Field from '$lib/components/ui/field'; import * as Field from '$lib/components/ui/field';
import * as Select from '$lib/components/ui/select';
import { m } from '$lib/paraglide/messages'; import { m } from '$lib/paraglide/messages';
import appConfigStore from '$lib/stores/application-configuration-store'; import appConfigStore from '$lib/stores/application-configuration-store';
import type { AllAppConfig } from '$lib/types/application-configuration'; import type { AllAppConfig } from '$lib/types/application-configuration.type';
import { preventDefault } from '$lib/utils/event-util'; import { preventDefault } from '$lib/utils/event-util';
import { createForm } from '$lib/utils/form-util'; import { createForm } from '$lib/utils/form-util';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
@@ -22,8 +23,14 @@
let isLoading = $state(false); let isLoading = $state(false);
const homePageUrlOptions = [
{ label: m.my_account(), value: '/settings/account' },
{ label: m.my_apps(), value: '/settings/apps' }
];
const updatedAppConfig = { const updatedAppConfig = {
appName: appConfig.appName, appName: appConfig.appName,
homePageUrl: appConfig.homePageUrl,
sessionDuration: appConfig.sessionDuration, sessionDuration: appConfig.sessionDuration,
emailsVerified: appConfig.emailsVerified, emailsVerified: appConfig.emailsVerified,
allowOwnAccountEdit: appConfig.allowOwnAccountEdit, allowOwnAccountEdit: appConfig.allowOwnAccountEdit,
@@ -33,6 +40,7 @@
const formSchema = z.object({ const formSchema = z.object({
appName: z.string().min(2).max(30), appName: z.string().min(2).max(30),
homePageUrl: z.string(),
sessionDuration: z.number().min(1).max(43200), sessionDuration: z.number().min(1).max(43200),
emailsVerified: z.boolean(), emailsVerified: z.boolean(),
allowOwnAccountEdit: z.boolean(), allowOwnAccountEdit: z.boolean(),
@@ -62,6 +70,32 @@
description={m.the_duration_of_a_session_in_minutes_before_the_user_has_to_sign_in_again()} description={m.the_duration_of_a_session_in_minutes_before_the_user_has_to_sign_in_again()}
bind:input={$inputs.sessionDuration} bind:input={$inputs.sessionDuration}
/> />
<Field.Field>
<Field.Label>{m.app_config_home_page()}</Field.Label>
<Field.Description>
{m.app_config_home_page_description()}
</Field.Description>
<Select.Root
type="single"
value={$inputs.homePageUrl.value}
onValueChange={(v) => ($inputs.homePageUrl.value = v as string)}
>
<Select.Trigger
class="w-full"
aria-label={m.app_config_home_page()}
>
{homePageUrlOptions.find((option) => option.value === $inputs.homePageUrl.value)
?.label ?? $inputs.homePageUrl.value}
</Select.Trigger>
<Select.Content>
{#each homePageUrlOptions as option}
<Select.Item value={option.value}>
{option.label}
</Select.Item>
{/each}
</Select.Content>
</Select.Root>
</Field.Field>
<SwitchWithLabel <SwitchWithLabel
id="self-account-editing" id="self-account-editing"
label={m.enable_self_account_editing()} label={m.enable_self_account_editing()}

View File

@@ -5,7 +5,7 @@
import { m } from '$lib/paraglide/messages'; import { m } from '$lib/paraglide/messages';
import AppConfigService from '$lib/services/app-config-service'; import AppConfigService from '$lib/services/app-config-service';
import appConfigStore from '$lib/stores/application-configuration-store'; import appConfigStore from '$lib/stores/application-configuration-store';
import type { AllAppConfig } from '$lib/types/application-configuration'; import type { AllAppConfig } from '$lib/types/application-configuration.type';
import { axiosErrorToast } from '$lib/utils/error-util'; import { axiosErrorToast } from '$lib/utils/error-util';
import { preventDefault } from '$lib/utils/event-util'; import { preventDefault } from '$lib/utils/event-util';
import { createForm } from '$lib/utils/form-util'; import { createForm } from '$lib/utils/form-util';

View File

@@ -6,7 +6,7 @@
import * as Select from '$lib/components/ui/select'; import * as Select from '$lib/components/ui/select';
import { m } from '$lib/paraglide/messages'; import { m } from '$lib/paraglide/messages';
import appConfigStore from '$lib/stores/application-configuration-store'; import appConfigStore from '$lib/stores/application-configuration-store';
import type { AllAppConfig } from '$lib/types/application-configuration'; import type { AllAppConfig } from '$lib/types/application-configuration.type';
import { preventDefault } from '$lib/utils/event-util'; import { preventDefault } from '$lib/utils/event-util';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';

View File

@@ -9,6 +9,10 @@ test.beforeEach(async ({ page }) => {
test('Update general configuration', async ({ page }) => { test('Update general configuration', async ({ page }) => {
await page.getByLabel('Application Name', { exact: true }).fill('Updated Name'); await page.getByLabel('Application Name', { exact: true }).fill('Updated Name');
await page.getByLabel('Session Duration').fill('30'); await page.getByLabel('Session Duration').fill('30');
await page.getByRole('button', { name: 'Home Page' }).click();
await page.getByRole('option', { name: 'My Apps' }).click();
await page.getByRole('button', { name: 'Save' }).first().click(); await page.getByRole('button', { name: 'Save' }).first().click();
await expect(page.locator('[data-type="success"]')).toHaveText( await expect(page.locator('[data-type="success"]')).toHaveText(
@@ -20,6 +24,9 @@ test('Update general configuration', async ({ page }) => {
await expect(page.getByLabel('Application Name', { exact: true })).toHaveValue('Updated Name'); await expect(page.getByLabel('Application Name', { exact: true })).toHaveValue('Updated Name');
await expect(page.getByLabel('Session Duration')).toHaveValue('30'); await expect(page.getByLabel('Session Duration')).toHaveValue('30');
await page.getByRole('link', { name: 'Logo Updated Name' }).click();
await page.waitForURL('/settings/apps');
}); });
test.describe('Update user creation configuration', () => { test.describe('Update user creation configuration', () => {
@@ -123,7 +130,9 @@ test.describe('Update application images', () => {
test('should upload images', async ({ page }) => { test('should upload images', async ({ page }) => {
await page.getByLabel('Favicon').setInputFiles('resources/images/w3-schools-favicon.ico'); await page.getByLabel('Favicon').setInputFiles('resources/images/w3-schools-favicon.ico');
await page.getByLabel('Light Mode Logo').setInputFiles('resources/images/pingvin-share-logo.png'); await page
.getByLabel('Light Mode Logo')
.setInputFiles('resources/images/pingvin-share-logo.png');
await page.getByLabel('Dark Mode Logo').setInputFiles('resources/images/cloud-logo.png'); await page.getByLabel('Dark Mode Logo').setInputFiles('resources/images/cloud-logo.png');
await page.getByLabel('Email Logo').setInputFiles('resources/images/pingvin-share-logo.png'); await page.getByLabel('Email Logo').setInputFiles('resources/images/pingvin-share-logo.png');
await page await page