Skip to content

Commit e76b59f

Browse files
authored
feat(config): added a configuration to add prefix to the view note url (#418)
1 parent 36d8b21 commit e76b59f

File tree

17 files changed

+147
-55
lines changed

17 files changed

+147
-55
lines changed

packages/app-client/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"script:get-missing-i18n-keys": "tsx src/scripts/get-missing-i18n-keys.script.ts"
2929
},
3030
"dependencies": {
31-
"@corentinth/chisels": "^1.1.0",
31+
"@corentinth/chisels": "catalog:",
3232
"@enclosed/lib": "workspace:*",
3333
"@kobalte/core": "^0.13.4",
3434
"@solid-primitives/i18n": "^2.1.1",

packages/app-client/src/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { render, Suspense } from 'solid-js/web';
66
import { I18nProvider } from './modules/i18n/i18n.provider';
77
import { NoteContextProvider } from './modules/notes/notes.context';
88
import { Toaster } from './modules/ui/components/sonner';
9-
import { routes } from './routes';
9+
import { getRoutes } from './routes';
1010
import '@unocss/reset/tailwind.css';
1111
import 'virtual:uno.css';
1212
import './app.css';
@@ -19,7 +19,7 @@ render(
1919

2020
return (
2121
<Router
22-
children={routes}
22+
children={getRoutes()}
2323
root={props => (
2424
<Suspense>
2525
<I18nProvider>

packages/app-client/src/modules/config/config.constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ export const buildTimeConfig: Config = {
99
defaultNoteTtlSeconds: Number(import.meta.env.VITE_DEFAULT_NOTE_TTL_SECONDS ?? 3600),
1010
defaultNoteNoExpiration: import.meta.env.VITE_DEFAULT_NOTE_NO_EXPIRATION === 'true',
1111
isSettingNoExpirationAllowed: import.meta.env.VITE_IS_SETTING_NO_EXPIRATION_ALLOWED === 'true',
12+
viewNotePathPrefix: import.meta.env.VITE_VIEW_NOTE_PATH_PREFIX,
1213
};

packages/app-client/src/modules/config/config.types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ export type Config = {
77
defaultNoteTtlSeconds: number;
88
isSettingNoExpirationAllowed: boolean;
99
defaultNoteNoExpiration: boolean;
10+
viewNotePathPrefix: string;
1011
};
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { describe, expect, test } from 'vitest';
2+
import { buildViewNotePagePath } from './notes.models';
3+
4+
describe('notes models', () => {
5+
describe('buildViewNotePagePath', () => {
6+
describe('when self-hosting, user can configure a prefix for the view note page', () => {
7+
test('for nullish or empty prefix, the path is /:noteId', () => {
8+
expect(buildViewNotePagePath({ prefix: null })).toBe('/:noteId');
9+
expect(buildViewNotePagePath({ prefix: '' })).toBe('/:noteId');
10+
expect(buildViewNotePagePath({ prefix: '/' })).toBe('/:noteId');
11+
expect(buildViewNotePagePath({ prefix: undefined })).toBe('/:noteId');
12+
expect(buildViewNotePagePath({ })).toBe('/:noteId');
13+
});
14+
15+
test('for a non-empty prefix, the prefix is prepended to the noteId param', () => {
16+
expect(buildViewNotePagePath({ prefix: 'test' })).toBe('/test/:noteId');
17+
expect(buildViewNotePagePath({ prefix: '/test' })).toBe('/test/:noteId');
18+
expect(buildViewNotePagePath({ prefix: 'test/' })).toBe('/test/:noteId');
19+
expect(buildViewNotePagePath({ prefix: '/test/' })).toBe('/test/:noteId');
20+
expect(buildViewNotePagePath({ prefix: 'test/prefix' })).toBe('/test/prefix/:noteId');
21+
expect(buildViewNotePagePath({ prefix: 'test/prefix/' })).toBe('/test/prefix/:noteId');
22+
expect(buildViewNotePagePath({ prefix: '/test/prefix' })).toBe('/test/prefix/:noteId');
23+
expect(buildViewNotePagePath({ prefix: '/test/prefix/' })).toBe('/test/prefix/:noteId');
24+
});
25+
});
26+
});
27+
});

packages/app-client/src/modules/notes/notes.models.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { joinUrlPaths } from '@corentinth/chisels';
12
import { sample, times } from 'lodash-es';
23

34
export { createRandomPassword };
@@ -11,3 +12,13 @@ function createRandomPassword({ length = 16 }: { length?: number } = {}): string
1112

1213
return times(length, () => sample(corpus)).join('');
1314
}
15+
16+
export function buildViewNotePagePath({ prefix }: { prefix?: string | null }): string {
17+
const noteIdParam = '/:noteId';
18+
19+
if (!prefix || prefix === '' || prefix === '/') {
20+
return noteIdParam;
21+
}
22+
23+
return `/${joinUrlPaths(prefix, noteIdParam)}`;
24+
}

packages/app-client/src/modules/notes/notes.usecases.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ async function encryptAndCreateNote(args: {
1010
deleteAfterReading: boolean;
1111
fileAssets: File[];
1212
isPublic?: boolean;
13+
pathPrefix?: string;
1314
}) {
1415
return createNote({
1516
...args,

packages/app-client/src/modules/notes/pages/create-note.page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ export const CreateNotePage: Component = () => {
165165
deleteAfterReading: getDeleteAfterReading(),
166166
fileAssets: getUploadedFiles(),
167167
isPublic: getIsPublic(),
168+
pathPrefix: config.viewNotePathPrefix,
168169
}));
169170

170171
setIsNoteCreating(false);

packages/app-client/src/routes.tsx

Lines changed: 44 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,54 @@
11
import { A, type RouteDefinition } from '@solidjs/router';
22
import { LoginPage } from './modules/auth/pages/login.page';
3+
import { getConfig } from './modules/config/config.provider';
34
import { NOTE_ID_REGEX } from './modules/notes/notes.constants';
5+
import { buildViewNotePagePath } from './modules/notes/notes.models';
46
import { CreateNotePage } from './modules/notes/pages/create-note.page';
57
import { ViewNotePage } from './modules/notes/pages/view-note.page';
68
import { Button } from './modules/ui/components/button';
79
import { AppLayout } from './modules/ui/layouts/app.layout';
810

9-
export const routes: RouteDefinition[] = [
10-
{
11-
path: '/',
12-
component: AppLayout,
13-
children: [
11+
export function getRoutes(): RouteDefinition[] {
12+
const config = getConfig();
13+
const viewNotePath = buildViewNotePagePath({ prefix: config.viewNotePathPrefix });
1414

15-
{
16-
path: '/',
17-
component: CreateNotePage,
18-
},
19-
{
20-
path: '/:noteId',
21-
component: ViewNotePage,
22-
matchFilters: {
23-
noteId: NOTE_ID_REGEX,
15+
return [
16+
{
17+
path: '/',
18+
component: AppLayout,
19+
children: [
20+
21+
{
22+
path: '/',
23+
component: CreateNotePage,
24+
},
25+
{
26+
path: viewNotePath,
27+
component: ViewNotePage,
28+
matchFilters: {
29+
noteId: NOTE_ID_REGEX,
30+
},
31+
},
32+
{
33+
path: '*404',
34+
component: () => (
35+
<div class="flex flex-col items-center justify-center mt-6">
36+
<div class="text-3xl font-light text-muted-foreground">404</div>
37+
<h1 class="font-semibold text-lg my-2">Page Not Found</h1>
38+
<p class="text-muted-foreground">The page you are looking for does not exist.</p>
39+
<p class="text-muted-foreground">Please check the URL and try again.</p>
40+
<Button as={A} href="/" class="mt-4" variant="secondary">
41+
<div class="i-tabler-arrow-left mr-2"></div>
42+
Go back home
43+
</Button>
44+
</div>
45+
),
2446
},
25-
},
26-
{
27-
path: '*404',
28-
component: () => (
29-
<div class="flex flex-col items-center justify-center mt-6">
30-
<div class="text-3xl font-light text-muted-foreground">404</div>
31-
<h1 class="font-semibold text-lg my-2">Page Not Found</h1>
32-
<p class="text-muted-foreground">The page you are looking for does not exist.</p>
33-
<p class="text-muted-foreground">Please check the URL and try again.</p>
34-
<Button as={A} href="/" class="mt-4" variant="secondary">
35-
<div class="i-tabler-arrow-left mr-2"></div>
36-
Go back home
37-
</Button>
38-
</div>
39-
),
40-
},
41-
],
42-
},
43-
{
44-
path: '/login',
45-
component: LoginPage,
46-
},
47-
];
47+
],
48+
},
49+
{
50+
path: '/login',
51+
component: LoginPage,
52+
},
53+
];
54+
}

packages/app-server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"script:generate-config-table": "tsx -r dotenv/config src/scripts/generate-config-table.script.ts"
2929
},
3030
"dependencies": {
31-
"@corentinth/chisels": "^1.1.0",
31+
"@corentinth/chisels": "catalog:",
3232
"@enclosed/lib": "workspace:*",
3333
"@hono/node-server": "^1.12.1",
3434
"bcryptjs": "^2.4.3",

0 commit comments

Comments
 (0)