Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions packages/app-client/src/modules/notes/notes.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@ export { storeNote, fetchNoteById };

async function storeNote({
payload,
isPasswordProtected,
ttlInSeconds,
deleteAfterReading,
encryptionAlgorithm,
serializationFormat,
}: {
payload: string;
isPasswordProtected: boolean;
ttlInSeconds: number;
deleteAfterReading: boolean;
encryptionAlgorithm: string;
Expand All @@ -22,7 +20,6 @@ async function storeNote({
method: 'POST',
body: {
payload,
isPasswordProtected,
ttlInSeconds,
deleteAfterReading,
serializationFormat,
Expand Down
17 changes: 11 additions & 6 deletions packages/app-client/src/modules/notes/pages/view-note.page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useLocation, useParams } from '@solidjs/router';
import { type Component, Match, Show, Switch, createSignal, onMount } from 'solid-js';
import { decryptNote, noteAssetsToFiles } from '@enclosed/lib';
import { decryptNote, noteAssetsToFiles, parseNoteUrlHashFragment } from '@enclosed/lib';
import JSZip from 'jszip';
import { formatBytes, safely } from '@corentinth/chisels';
import { fetchNoteById } from '../notes.services';
Expand Down Expand Up @@ -74,14 +74,19 @@ export const ViewNotePage: Component = () => {
const [fileAssets, setFileAssets] = createSignal<File[]>([]);
const [isDownloadingAllLoading, setIsDownloadingAllLoading] = createSignal(false);

const getEncryptionKey = () => location.hash.slice(1);
const parseHashFragment = () => parseNoteUrlHashFragment({ hashFragment: location.hash });
const getEncryptionKey = () => parseHashFragment().encryptionKey;
const getIsPasswordProtected = () => parseHashFragment().isPasswordProtected;

onMount(async () => {
if (!getEncryptionKey()) {
const encryptionKey = getEncryptionKey();

if (!encryptionKey) {
setError({
title: 'Invalid note URL',
description: 'This note URL is invalid. Please make sure you are using the correct URL.',
});
return;
}

const [fetchedNote, fetchError] = await safely(fetchNoteById({ noteId: params.noteId }));
Expand Down Expand Up @@ -114,15 +119,15 @@ export const ViewNotePage: Component = () => {

setNote(note);

if (note.isPasswordProtected) {
if (getIsPasswordProtected()) {
return;
}

const { payload, encryptionAlgorithm, serializationFormat } = note;

const [decryptedNoteResult, decryptionError] = await safely(decryptNote({
encryptedPayload: payload,
encryptionKey: getEncryptionKey(),
encryptionKey,
encryptionAlgorithm: encryptionAlgorithm as 'aes-256-gcm', // TODO: export type from lib
serializationFormat: serializationFormat as 'cbor-array', // TODO: export type from lib
}));
Expand Down Expand Up @@ -209,7 +214,7 @@ export const ViewNotePage: Component = () => {
)}
</Match>

<Match when={getNote()?.isPasswordProtected && !isPasswordEntered()}>
<Match when={getIsPasswordProtected() && !isPasswordEntered()}>
<RequestPasswordForm onPasswordEntered={onPasswordEntered} getIsPasswordInvalid={getIsPasswordInvalid} setIsPasswordInvalid={setIsPasswordInvalid} />
</Match>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ describe('e2e', () => {

const note = {
payload: '<encrypted-content>',
isPasswordProtected: false,
deleteAfterReading: false,
ttlInSeconds: 600,
encryptionAlgorithm: 'aes-256-gcm',
Expand Down Expand Up @@ -45,7 +44,6 @@ describe('e2e', () => {

expect(omit(retrievedNote, 'expirationDate')).to.eql({
payload: '<encrypted-content>',
isPasswordProtected: false,
encryptionAlgorithm: 'aes-256-gcm',
serializationFormat: 'cbor-array',
});
Expand All @@ -64,7 +62,6 @@ describe('e2e', () => {
method: 'POST',
body: JSON.stringify({
payload: '<encrypted-content>',
isPasswordProtected: false,
deleteAfterReading: false,
ttlInSeconds: 600,
encryptionAlgorithm: 'aes-256-gcm',
Expand Down Expand Up @@ -102,7 +99,6 @@ describe('e2e', () => {
method: 'POST',
body: JSON.stringify({
payload: '<encrypted-content>',
isPasswordProtected: false,
deleteAfterReading: false,
ttlInSeconds: 600,
encryptionAlgorithm: 'foo', // <- invalid encryption algorithm
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ describe('e2e', () => {
});

const note = {
isPasswordProtected: false,
deleteAfterReading: false,
ttlInSeconds: 600,
payload: 'a'.repeat(1024 * 1024 + 1),
Expand Down
2 changes: 0 additions & 2 deletions packages/app-server/src/modules/notes/notes.models.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ describe('notes models', () => {
test('the expiration date and the flag stating the note should be deleted after read are omitted when formatting a note for the API', () => {
const storedNote = {
payload: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: new Date('2024-01-01T00:00:00Z'),
deleteAfterReading: false,
serializationFormat: 'cbor-array',
Expand All @@ -43,7 +42,6 @@ describe('notes models', () => {
expect(formatNoteForApi({ note: storedNote })).to.eql({
apiNote: {
payload: '<encrypted-content>',
isPasswordProtected: false,
encryptionAlgorithm: 'aes-256-gcm',
serializationFormat: 'cbor-array',
},
Expand Down
10 changes: 0 additions & 10 deletions packages/app-server/src/modules/notes/notes.repository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ describe('notes repository', () => {

storage.setItem('note-1', {
payload: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: '2024-01-01T00:01:00.000Z',
deleteAfterReading: false,
encryptionAlgorithm: 'aes-256-gcm',
Expand All @@ -23,7 +22,6 @@ describe('notes repository', () => {

expect(note).to.eql({
payload: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: new Date('2024-01-01T00:01:00.000Z'),
deleteAfterReading: false,
encryptionAlgorithm: 'aes-256-gcm',
Expand All @@ -36,7 +34,6 @@ describe('notes repository', () => {

storage.setItem('note-1', {
payload: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: '2024-01-01T00:01:00.000Z',
deleteAfterReading: false,
encryptionAlgorithm: 'aes-256-gcm',
Expand All @@ -55,7 +52,6 @@ describe('notes repository', () => {

storage.setItem('note-1', {
payload: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: '2024-01-01T00:01:00.000Z',
deleteAfterReading: false,
encryptionAlgorithm: 'aes-256-gcm',
Expand All @@ -64,7 +60,6 @@ describe('notes repository', () => {

storage.setItem('note-2', {
payload: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: '2024-01-01T00:01:00.000Z',
deleteAfterReading: false,
encryptionAlgorithm: 'aes-256-gcm',
Expand Down Expand Up @@ -93,7 +88,6 @@ describe('notes repository', () => {

storage.setItem('note-1', {
payload: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: '2024-01-01T00:01:00.000Z',
deleteAfterReading: true,
encryptionAlgorithm: 'aes-256-gcm',
Expand All @@ -116,7 +110,6 @@ describe('notes repository', () => {

storage.setItem('note-1', {
payload: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: '2024-01-01T00:01:00.000Z',
deleteAfterReading: false,
encryptionAlgorithm: 'aes-256-gcm',
Expand All @@ -125,7 +118,6 @@ describe('notes repository', () => {

storage.setItem('note-2', {
payload: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: '2024-01-01T00:01:00.000Z',
deleteAfterReading: false,
encryptionAlgorithm: 'aes-256-gcm',
Expand Down Expand Up @@ -163,7 +155,6 @@ describe('notes repository', () => {

const { noteId } = await saveNote({
payload: '<encrypted-content>',
isPasswordProtected: false,
ttlInSeconds: 60,
deleteAfterReading: false,
generateNoteId: () => `note-${noteIdIndex++}`,
Expand All @@ -177,7 +168,6 @@ describe('notes repository', () => {

expect(await storage.getItem<any>('note-1')).to.eql({
payload: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: '2024-01-01T00:01:00.000Z',
deleteAfterReading: false,
encryptionAlgorithm: 'aes-256-gcm',
Expand Down
3 changes: 0 additions & 3 deletions packages/app-server/src/modules/notes/notes.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ async function getNotesIds({ storage }: { storage: Storage }) {
async function saveNote(
{
payload,
isPasswordProtected,
ttlInSeconds,
deleteAfterReading,
storage,
Expand All @@ -41,7 +40,6 @@ async function saveNote(
}:
{
payload: string;
isPasswordProtected: boolean;
ttlInSeconds: number;
deleteAfterReading: boolean;
storage: Storage;
Expand All @@ -58,7 +56,6 @@ async function saveNote(
noteId,
{
payload,
isPasswordProtected,
expirationDate: expirationDate.toISOString(),
deleteAfterReading,
encryptionAlgorithm,
Expand Down
5 changes: 2 additions & 3 deletions packages/app-server/src/modules/notes/notes.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ function setupCreateNoteRoute({ app }: { app: ServerInstance }) {
validateJsonBody(
z.object({
payload: z.string(),
isPasswordProtected: z.boolean(),
deleteAfterReading: z.boolean(),
ttlInSeconds: z.number()
.min(TEN_MINUTES_IN_SECONDS)
Expand All @@ -62,12 +61,12 @@ function setupCreateNoteRoute({ app }: { app: ServerInstance }) {
},

async (context) => {
const { payload, isPasswordProtected, ttlInSeconds, deleteAfterReading, encryptionAlgorithm, serializationFormat } = context.req.valid('json');
const { payload, ttlInSeconds, deleteAfterReading, encryptionAlgorithm, serializationFormat } = context.req.valid('json');
const storage = context.get('storage');

const notesRepository = createNoteRepository({ storage });

const { noteId } = await notesRepository.saveNote({ payload, isPasswordProtected, ttlInSeconds, deleteAfterReading, encryptionAlgorithm, serializationFormat });
const { noteId } = await notesRepository.saveNote({ payload, ttlInSeconds, deleteAfterReading, encryptionAlgorithm, serializationFormat });

return context.json({ noteId });
},
Expand Down
4 changes: 0 additions & 4 deletions packages/app-server/src/modules/notes/notes.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,4 @@ export type StoredNote = {
// compressionAlgorithm: string
// keyDerivationAlgorithm: string;

/**
* @deprecated Password protection information should be stored in the url
*/
isPasswordProtected: boolean;
};
5 changes: 0 additions & 5 deletions packages/app-server/src/modules/notes/notes.usecases.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ describe('notes usecases', () => {

storage.setItem('note-1', {
content: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: '2024-01-01T00:01:00.000Z',
deleteAfterReading: false,
});
Expand All @@ -24,7 +23,6 @@ describe('notes usecases', () => {

expect(note).to.eql({
content: '<encrypted-content>',
isPasswordProtected: false,
deleteAfterReading: false,
expirationDate: new Date('2024-01-01T00:01:00.000Z'),
});
Expand All @@ -35,7 +33,6 @@ describe('notes usecases', () => {

storage.setItem('note-1', {
content: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: '2024-01-01T00:01:00.000Z',
deleteAfterReading: false,
});
Expand All @@ -54,7 +51,6 @@ describe('notes usecases', () => {

storage.setItem('note-1', {
content: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: '2024-01-01T00:00:00.000Z',
deleteAfterReading: false,
});
Expand All @@ -73,7 +69,6 @@ describe('notes usecases', () => {

storage.setItem('note-1', {
content: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: '2024-01-02T00:00:00.000Z',
deleteAfterReading: true,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,18 @@ describe('delete-expired-notes tasks', () => {

storage.setItem('note-1', {
content: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: '2024-01-01T00:01:00.000Z',
deleteAfterReading: false,
});

storage.setItem('note-2', {
content: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: '2024-01-02T00:00:00.000Z',
deleteAfterReading: false,
});

storage.setItem('note-3', {
content: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: '2024-01-03T00:00:00.000Z',
deleteAfterReading: false,
});
Expand All @@ -49,21 +46,18 @@ describe('delete-expired-notes tasks', () => {

storage.setItem('note-1', {
content: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: '2024-01-01T00:01:00.000Z',
deleteAfterReading: false,
});

storage.setItem('note-2', {
content: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: '2024-01-02T00:00:00.000Z',
deleteAfterReading: false,
});

storage.setItem('note-3', {
content: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: '2024-01-03T00:00:00.000Z',
deleteAfterReading: false,
});
Expand Down Expand Up @@ -100,21 +94,18 @@ describe('delete-expired-notes tasks', () => {

storage.setItem('note-1', {
content: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: '2024-01-01T00:01:00.000Z',
deleteAfterReading: false,
});

storage.setItem('note-2', {
content: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: '2024-01-02T00:00:00.000Z',
deleteAfterReading: false,
});

storage.setItem('note-3', {
content: '<encrypted-content>',
isPasswordProtected: false,
expirationDate: '2024-01-03T00:00:00.000Z',
deleteAfterReading: false,
});
Expand Down
2 changes: 1 addition & 1 deletion packages/lib/src/api/api.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ async function apiClient<T>({
baseURL: baseUrl,
onResponseError: async ({ response }) => {
throw Object.assign(
new Error('Failed to fetch note'),
new Error('Failed to make API request'),
{
response: {
status: response.status,
Expand Down
Loading
Loading