mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-02-04 11:36:46 +00:00
feat: make home page URL configurable (#1215)
This commit is contained in:
@@ -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"`
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"},
|
||||||
|
|||||||
@@ -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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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 () => {
|
||||||
|
|||||||
@@ -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);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|||||||
@@ -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()}
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user