refactor: replace TypeORM with knex.js

Co-authored-by: Philip Molares <philip.molares@udo.edu>
Signed-off-by: Philip Molares <philip.molares@udo.edu>
Signed-off-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
Erik Michelson 2025-03-14 23:33:29 +01:00
parent 6e151c8a1b
commit c0ce00b3f9
No known key found for this signature in database
GPG key ID: DB99ADDDC5C0AF82
242 changed files with 4601 additions and 6871 deletions

View file

@ -3,10 +3,10 @@
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { ProviderType } from '../../src/api/config/types'
import { ProviderType } from '../../src/api/config/types'
import type { AuthProviderType } from '../../src/api/config/types'
import { AuthProviderType } from '../../src/api/config/types'
const initLoggedOutTestWithCustomAuthProviders = (cy: Cypress.cy, enabledProviders: ProviderType[]) => {
const initLoggedOutTestWithCustomAuthProviders = (cy: Cypress.cy, enabledProviders: AuthProviderType[]) => {
cy.logOut()
cy.loadConfig({
authProviders: enabledProviders
@ -48,7 +48,7 @@ describe('When logged-out ', () => {
it('sign-in button points to login route: internal', () => {
initLoggedOutTestWithCustomAuthProviders(cy, [
{
type: ProviderType.LOCAL
type: AuthProviderType.LOCAL
}
])
cy.getByCypressId('sign-in-button')
@ -60,7 +60,7 @@ describe('When logged-out ', () => {
it('sign-in button points to login route: ldap', () => {
initLoggedOutTestWithCustomAuthProviders(cy, [
{
type: ProviderType.LDAP,
type: AuthProviderType.LDAP,
identifier: 'cy-ldap',
providerName: 'cy LDAP'
}
@ -76,7 +76,7 @@ describe('When logged-out ', () => {
it('sign-in button points to auth-provider', () => {
initLoggedOutTestWithCustomAuthProviders(cy, [
{
type: ProviderType.OIDC,
type: AuthProviderType.OIDC,
identifier: 'github',
providerName: 'GitHub',
theme: 'github'
@ -94,13 +94,13 @@ describe('When logged-out ', () => {
it('sign-in button points to login route', () => {
initLoggedOutTestWithCustomAuthProviders(cy, [
{
type: ProviderType.OIDC,
type: AuthProviderType.OIDC,
identifier: 'github',
providerName: 'GitHub',
theme: 'github'
},
{
type: ProviderType.OIDC,
type: AuthProviderType.OIDC,
identifier: 'gitlab',
providerName: 'GitLab',
theme: 'gitlab'
@ -117,13 +117,13 @@ describe('When logged-out ', () => {
it('sign-in button points to login route', () => {
initLoggedOutTestWithCustomAuthProviders(cy, [
{
type: ProviderType.OIDC,
type: AuthProviderType.OIDC,
identifier: 'github',
providerName: 'GitHub',
theme: 'github'
},
{
type: ProviderType.LOCAL
type: AuthProviderType.LOCAL
}
])
cy.getByCypressId('sign-in-button')

View file

@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { ProviderType } from '@hedgedoc/commons'
import { AuthProviderType } from '@hedgedoc/commons'
import { HttpMethod } from '../../src/handler-utils/respond-to-matching-request'
import { IGNORE_MOTD, MOTD_LOCAL_STORAGE_KEY } from '../../src/components/global-dialogs/motd-modal/local-storage-keys'
@ -22,15 +22,15 @@ export const branding = {
export const authProviders = [
{
type: ProviderType.LOCAL
type: AuthProviderType.LOCAL
},
{
type: ProviderType.LDAP,
type: AuthProviderType.LDAP,
identifier: 'test-ldap',
providerName: 'Test LDAP'
},
{
type: ProviderType.OIDC,
type: AuthProviderType.OIDC,
identifier: 'test-oidc',
providerName: 'Test OIDC'
}

View file

@ -15,12 +15,12 @@ const mockMetadata = {
noteId: testNoteId
}
],
primaryAddress: 'mock-note',
primaryAlias: 'mock-note',
title: 'Mock Note',
description: 'Mocked note for testing',
tags: ['test', 'mock', 'cypress'],
updatedAt: '2021-04-24T09:27:51.000Z',
updateUsername: null,
lastUpdatedBy: null,
viewCount: 0,
version: 2,
createdAt: '2021-04-24T09:27:51.000Z',

View file

@ -11,15 +11,15 @@ import type { AliasDto, AliasCreateDto, AliasUpdateDto } from '@hedgedoc/commons
/**
* Adds an alias to an existing note.
*
* @param noteIdOrAlias The note id or an existing alias for a note.
* @param noteAlias The note id or an existing alias for a note.
* @param newAlias The new alias.
* @return Information about the newly created alias.
* @throws {Error} when the api request wasn't successful
*/
export const addAlias = async (noteIdOrAlias: string, newAlias: string): Promise<AliasDto> => {
export const addAlias = async (noteAlias: string, newAlias: string): Promise<AliasDto> => {
const response = await new PostApiRequestBuilder<AliasDto, AliasCreateDto>('alias')
.withJsonBody({
noteIdOrAlias,
noteAlias,
newAlias
})
.sendRequest()

View file

@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { FullUserInfoDto } from '@hedgedoc/commons'
import type { PendingUserInfoDto } from '@hedgedoc/commons'
import { GetApiRequestBuilder } from '../common/api-request-builder/get-api-request-builder'
import { DeleteApiRequestBuilder } from '../common/api-request-builder/delete-api-request-builder'
import type { PendingUserConfirmationDto } from '@hedgedoc/commons'
@ -13,8 +13,8 @@ import { PutApiRequestBuilder } from '../common/api-request-builder/put-api-requ
* Fetches the pending user information.
* @returns The pending user information.
*/
export const getPendingUserInfo = async (): Promise<FullUserInfoDto> => {
const response = await new GetApiRequestBuilder<FullUserInfoDto>('auth/pending-user').sendRequest()
export const getPendingUserInfo = async (): Promise<PendingUserInfoDto> => {
const response = await new GetApiRequestBuilder<PendingUserInfoDto>('auth/pending-user').sendRequest()
return response.asParsedJsonObject()
}

View file

@ -15,7 +15,7 @@ import type { RevisionDto, RevisionMetadataDto } from '@hedgedoc/commons'
* @return The revision.
* @throws {Error} when the api request wasn't successful.
*/
export const getRevision = async (noteId: string, revisionId: number): Promise<RevisionDto> => {
export const getRevision = async (noteId: string, revisionId: string): Promise<RevisionDto> => {
const response = await new GetApiRequestBuilder<RevisionDto>(`notes/${noteId}/revisions/${revisionId}`).sendRequest()
return response.asParsedJsonObject()
}

View file

@ -36,7 +36,7 @@ const NewNotePage: NextPage = () => {
descriptionI18nKey={'errors.noteCreationFailed.description'}
/>
}>
{value !== undefined && <Redirect to={`/n/${value.metadata.primaryAddress}`} replace={true} />}
{value !== undefined && <Redirect to={`/n/${value.metadata.primaryAlias}`} replace={true} />}
</CustomAsyncLoadingBoundary>
)
}

View file

@ -11,7 +11,7 @@
SPDX-License-Identifier: AGPL-3.0-only
*/
import { ProviderType } from '@hedgedoc/commons'
import { AuthProviderType } from '@hedgedoc/commons'
import { Redirect } from '../../../components/common/redirect'
import { LandingLayout } from '../../../components/landing-layout/landing-layout'
import { ProfileAccessTokens } from '../../../components/profile-page/access-tokens/profile-access-tokens'
@ -40,7 +40,7 @@ const ProfilePage: NextPage = () => {
<Row className='h-100 flex justify-content-center'>
<Col lg={6}>
<ProfileDisplayName />
{userProvider === ProviderType.LOCAL && <ProfileChangePassword />}
{userProvider === AuthProviderType.LOCAL && <ProfileChangePassword />}
<ProfileAccessTokens />
<ProfileAccountManagement />
</Col>

View file

@ -6,8 +6,10 @@
import { measurePerformance } from '../../../utils/measure-performance'
import type { ParserOptions } from '@hedgedoc/html-to-react'
import { convertHtmlToReact } from '@hedgedoc/html-to-react'
import type DOMPurify from 'dompurify'
import { sanitize } from 'dompurify'
import DOMPurify from 'dompurify'
// see https://github.com/cure53/DOMPurify/issues/1034#issuecomment-2493211056
// eslint-disable-next-line @typescript-eslint/unbound-method
const { sanitize } = DOMPurify
import React, { Fragment, useMemo } from 'react'
export interface HtmlToReactProps {

View file

@ -12,7 +12,7 @@ import React, { useCallback } from 'react'
import { FileEarmarkPlus as IconPlus } from 'react-bootstrap-icons'
import { Trans } from 'react-i18next'
import { useFrontendConfig } from '../frontend-config-context/use-frontend-config'
import { GuestAccess } from '@hedgedoc/commons'
import { PermissionLevel } from '@hedgedoc/commons'
import { useIsLoggedIn } from '../../../hooks/common/use-is-logged-in'
/**
@ -27,14 +27,14 @@ export const NewNoteButton: React.FC = () => {
const createNewNoteAndRedirect = useCallback((): void => {
createNote('')
.then((note) => {
router?.push(`/n/${note.metadata.primaryAddress}`)
router?.push(`/n/${note.metadata.primaryAlias}`)
})
.catch((error: Error) => {
showErrorNotification(error.message)
})
}, [router, showErrorNotification])
if (!isLoggedIn && guestAccessLevel !== GuestAccess.CREATE) {
if (!isLoggedIn && guestAccessLevel !== PermissionLevel.CREATE) {
return null
}

View file

@ -23,7 +23,7 @@ describe('create non existing note hint', () => {
.mockImplementation(async (markdown, primaryAlias): Promise<NoteDto> => {
expect(markdown).toBe('')
expect(primaryAlias).toBe(mockedNoteId)
const metadata: NoteMetadataDto = Mock.of<NoteMetadataDto>({ primaryAddress: 'mockedPrimaryAlias' })
const metadata: NoteMetadataDto = Mock.of<NoteMetadataDto>({ primaryAlias: 'mockedPrimaryAlias' })
await new Promise((resolve) => setTimeout(resolve, 100))
await waitForOtherPromisesToFinish()
return Mock.of<NoteDto>({ metadata })

View file

@ -11,7 +11,6 @@ import { NoteInfoLineUpdatedBy } from '../editor-page/sidebar/specific-sidebar-e
import styles from './document-infobar.module.scss'
import React from 'react'
import { Pencil as IconPencil } from 'react-bootstrap-icons'
import { Trans } from 'react-i18next'
/**
* Renders an info bar with metadata about the current note.
@ -34,10 +33,9 @@ export const DocumentInfobar: React.FC = () => {
<hr />
</div>
<span className={'ms-auto'}>
{noteDetails.viewCount} <Trans i18nKey={'views.readOnly.viewCount'} />
<InternalLink
text={''}
href={`/n/${noteDetails.primaryAddress}`}
href={`/n/${noteDetails.primaryAlias}`}
icon={IconPencil}
className={'text-primary text-decoration-none mx-1'}
title={linkTitle}

View file

@ -53,14 +53,14 @@ export const useHandleUpload = (): handleUploadSignature => {
: t('editor.upload.uploadFile.withoutDescription', { fileName: file.name })
const uploadPlaceholder = `![${uploadFileInfo}](upload-${randomId}${additionalUrlText ?? ''})`
const noteId = getGlobalState().noteDetails?.id
if (noteId === undefined) {
const noteAlias = getGlobalState().noteDetails?.primaryAlias
if (noteAlias === undefined) {
return
}
changeContent(({ currentSelection }) => {
return replaceSelection(cursorSelection ?? currentSelection, uploadPlaceholder, false)
})
uploadFile(noteId, file)
uploadFile(noteAlias, file)
.then(({ uuid }) => {
const fullUrl = `${baseUrl}media/${uuid}`
const replacement = `![${description ?? file.name ?? ''}](${fullUrl}${additionalUrlText ?? ''})`

View file

@ -14,7 +14,7 @@ const LOCAL_FALLBACK_URL = 'ws://localhost:8080/realtime/'
* Provides the URL for the realtime endpoint.
*/
export const useWebsocketUrl = (): URL | null => {
const noteId = useApplicationState((state) => state.noteDetails?.id)
const noteAlias = useApplicationState((state) => state.noteDetails?.primaryAlias)
const baseUrl = useBaseUrl()
const websocketUrl = useMemo(() => {
@ -33,11 +33,11 @@ export const useWebsocketUrl = (): URL | null => {
}, [baseUrl])
return useMemo(() => {
if (noteId === '' || noteId === undefined) {
if (noteAlias === '' || noteAlias === undefined) {
return null
}
const url = new URL(websocketUrl)
url.search = `?noteId=${noteId}`
url.search = `?noteAlias=${noteAlias}`
return url
}, [noteId, websocketUrl])
}, [noteAlias, websocketUrl])
}

View file

@ -34,7 +34,7 @@ export const MediaBrowserSidebarMenu: React.FC<SpecificSidebarMenuProps> = ({
selectedMenuId
}) => {
useTranslation()
const noteId = useApplicationState((state) => state.noteDetails?.id ?? '')
const noteAlias = useApplicationState((state) => state.noteDetails?.primaryAlias ?? '')
const [mediaEntryForDeletion, setMediaEntryForDeletion] = useState<MediaUploadDto | null>(null)
const hide = selectedMenuId !== DocumentSidebarMenuSelection.NONE && selectedMenuId !== menuId
@ -43,7 +43,7 @@ export const MediaBrowserSidebarMenu: React.FC<SpecificSidebarMenuProps> = ({
onClick(menuId)
}, [menuId, onClick])
const { value, loading, error } = useAsync(() => getMediaForNote(noteId), [expand, noteId])
const { value, loading, error } = useAsync(() => getMediaForNote(noteAlias), [expand, noteAlias])
const mediaEntries = useMemo(() => {
if (loading || error || !value) {

View file

@ -15,7 +15,7 @@ import { Trans, useTranslation } from 'react-i18next'
*/
export const NoteInfoLineUpdatedBy: React.FC = () => {
useTranslation()
const noteUpdateUser = useApplicationState((state) => state.noteDetails?.updateUsername)
const noteUpdateUser = useApplicationState((state) => state.noteDetails?.lastUpdatedBy)
const userBlock = useMemo(() => {
if (!noteUpdateUser) {

View file

@ -6,7 +6,7 @@
import { useTranslatedText } from '../../../../../../hooks/common/use-translated-text'
import { UiIcon } from '../../../../../common/icons/ui-icon'
import type { PermissionDisabledProps } from './permission-disabled.prop'
import { GuestAccess } from '@hedgedoc/commons'
import { PermissionLevel } from '@hedgedoc/commons'
import React, { useMemo } from 'react'
import { Button, ToggleButtonGroup } from 'react-bootstrap'
import { Eye as IconEye, Pencil as IconPencil, X as IconX } from 'react-bootstrap-icons'
@ -24,7 +24,7 @@ export enum PermissionType {
export interface PermissionEntryButtonsProps {
type: PermissionType
currentSetting: GuestAccess
currentSetting: PermissionLevel
name: string
onSetReadOnly: () => void
onSetWriteable: () => void
@ -79,14 +79,14 @@ export const PermissionEntryButtons: React.FC<PermissionEntryButtonsProps & Perm
<Button
disabled={disabled}
title={setReadOnlyTitle}
variant={currentSetting === GuestAccess.READ ? 'secondary' : 'outline-secondary'}
variant={currentSetting === PermissionLevel.READ ? 'secondary' : 'outline-secondary'}
onClick={onSetReadOnly}>
<UiIcon icon={IconEye} />
</Button>
<Button
disabled={disabled}
title={setWritableTitle}
variant={currentSetting === GuestAccess.WRITE ? 'secondary' : 'outline-secondary'}
variant={currentSetting === PermissionLevel.WRITE ? 'secondary' : 'outline-secondary'}
onClick={onSetWriteable}>
<UiIcon icon={IconPencil} />
</Button>

View file

@ -10,7 +10,7 @@ import { setNotePermissionsFromServer } from '../../../../../../redux/note-detai
import { IconButton } from '../../../../../common/icon-button/icon-button'
import { useUiNotifications } from '../../../../../notifications/ui-notification-boundary'
import type { PermissionDisabledProps } from './permission-disabled.prop'
import { GuestAccess, SpecialGroup } from '@hedgedoc/commons'
import { PermissionLevel, SpecialGroup } from '@hedgedoc/commons'
import React, { useCallback, useMemo } from 'react'
import { ToggleButtonGroup } from 'react-bootstrap'
import { Eye as IconEye, Pencil as IconPencil, SlashCircle as IconSlashCircle } from 'react-bootstrap-icons'
@ -19,7 +19,7 @@ import { PermissionInconsistentAlert } from './permission-inconsistent-alert'
import { cypressId } from '../../../../../../utils/cypress-attribute'
export interface PermissionEntrySpecialGroupProps {
level: GuestAccess
level: PermissionLevel
type: SpecialGroup
inconsistent?: boolean
}
@ -38,7 +38,7 @@ export const PermissionEntrySpecialGroup: React.FC<PermissionEntrySpecialGroupPr
disabled,
inconsistent
}) => {
const noteId = useApplicationState((state) => state.noteDetails?.primaryAddress)
const noteId = useApplicationState((state) => state.noteDetails?.primaryAlias)
const { t } = useTranslation()
const { showErrorNotification } = useUiNotifications()
@ -98,7 +98,7 @@ export const PermissionEntrySpecialGroup: React.FC<PermissionEntrySpecialGroupPr
<IconButton
icon={IconSlashCircle}
title={denyGroupText}
variant={level === GuestAccess.DENY ? 'secondary' : 'outline-secondary'}
variant={level === PermissionLevel.DENY ? 'secondary' : 'outline-secondary'}
onClick={onSetEntryDenied}
disabled={disabled}
className={'p-1'}
@ -107,7 +107,7 @@ export const PermissionEntrySpecialGroup: React.FC<PermissionEntrySpecialGroupPr
<IconButton
icon={IconEye}
title={viewOnlyGroupText}
variant={level === GuestAccess.READ ? 'secondary' : 'outline-secondary'}
variant={level === PermissionLevel.READ ? 'secondary' : 'outline-secondary'}
onClick={onSetEntryReadOnly}
disabled={disabled}
className={'p-1'}
@ -116,7 +116,7 @@ export const PermissionEntrySpecialGroup: React.FC<PermissionEntrySpecialGroupPr
<IconButton
icon={IconPencil}
title={editGroupText}
variant={level === GuestAccess.WRITE ? 'secondary' : 'outline-secondary'}
variant={level === PermissionLevel.WRITE ? 'secondary' : 'outline-secondary'}
onClick={onSetEntryWriteable}
disabled={disabled}
className={'p-1'}

View file

@ -11,7 +11,7 @@ import { useUiNotifications } from '../../../../../notifications/ui-notification
import type { PermissionDisabledProps } from './permission-disabled.prop'
import { PermissionEntryButtons, PermissionType } from './permission-entry-buttons'
import type { NoteUserPermissionEntryDto } from '@hedgedoc/commons'
import { GuestAccess, SpecialGroup } from '@hedgedoc/commons'
import { PermissionLevel, SpecialGroup } from '@hedgedoc/commons'
import React, { useCallback, useMemo } from 'react'
import { useAsync } from 'react-use'
import { PermissionInconsistentAlert } from './permission-inconsistent-alert'
@ -33,7 +33,7 @@ export const PermissionEntryUser: React.FC<PermissionEntryUserProps & Permission
entry,
disabled
}) => {
const noteId = useApplicationState((state) => state.noteDetails?.primaryAddress)
const noteId = useApplicationState((state) => state.noteDetails?.primaryAlias)
const { showErrorNotification } = useUiNotifications()
const { [SpecialGroup.EVERYONE]: everyonePermission, [SpecialGroup.LOGGED_IN]: loggedInPermission } =
useGetSpecialPermissions()
@ -94,7 +94,7 @@ export const PermissionEntryUser: React.FC<PermissionEntryUserProps & Permission
<PermissionInconsistentAlert show={permissionInconsistent ?? false} />
<PermissionEntryButtons
type={PermissionType.USER}
currentSetting={entry.canEdit ? GuestAccess.WRITE : GuestAccess.READ}
currentSetting={entry.canEdit ? PermissionLevel.WRITE : PermissionLevel.READ}
name={value.displayName}
onSetReadOnly={onSetEntryReadOnly}
onSetWriteable={onSetEntryWriteable}

View file

@ -6,7 +6,7 @@
import { useIsOwner } from '../../../../../../hooks/common/use-is-owner'
import type { PermissionDisabledProps } from './permission-disabled.prop'
import { PermissionEntrySpecialGroup } from './permission-entry-special-group'
import { GuestAccess, SpecialGroup } from '@hedgedoc/commons'
import { PermissionLevel, SpecialGroup } from '@hedgedoc/commons'
import React, { Fragment, useMemo } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { useGetSpecialPermissions } from './hooks/use-get-special-permissions'
@ -23,8 +23,16 @@ export const PermissionSectionSpecialGroups: React.FC<PermissionDisabledProps> =
const specialGroupEntries = useMemo(() => {
return {
everyoneLevel: groupEveryone ? (groupEveryone.canEdit ? GuestAccess.WRITE : GuestAccess.READ) : GuestAccess.DENY,
loggedInLevel: groupLoggedIn ? (groupLoggedIn.canEdit ? GuestAccess.WRITE : GuestAccess.READ) : GuestAccess.DENY,
everyoneLevel: groupEveryone
? groupEveryone.canEdit
? PermissionLevel.WRITE
: PermissionLevel.READ
: PermissionLevel.DENY,
loggedInLevel: groupLoggedIn
? groupLoggedIn.canEdit
? PermissionLevel.WRITE
: PermissionLevel.READ
: PermissionLevel.DENY,
loggedInInconsistentAlert: groupEveryone && (!groupLoggedIn || (groupEveryone.canEdit && !groupLoggedIn.canEdit))
}
}, [groupEveryone, groupLoggedIn])

View file

@ -12,11 +12,11 @@ import React, { useMemo } from 'react'
import { ListGroup } from 'react-bootstrap'
interface RevisionListProps {
selectedRevisionId?: number
selectedRevisionId?: string
revisions?: RevisionMetadataDto[]
loadingRevisions: boolean
error?: Error | boolean
onRevisionSelect: (selectedRevisionId: number) => void
onRevisionSelect: (selectedRevisionId: string) => void
}
/**
@ -47,10 +47,10 @@ export const RevisionList: React.FC<RevisionListProps> = ({
})
.map((revisionListEntry) => (
<RevisionListEntry
active={selectedRevisionId === revisionListEntry.id}
onSelect={() => onRevisionSelect(revisionListEntry.id)}
active={selectedRevisionId === revisionListEntry.uuid}
onSelect={() => onRevisionSelect(revisionListEntry.uuid)}
revision={revisionListEntry}
key={revisionListEntry.id}
key={revisionListEntry.uuid}
/>
))
}, [loadingRevisions, onRevisionSelect, revisions, selectedRevisionId])

View file

@ -26,18 +26,18 @@ import { useAsync } from 'react-use'
export const RevisionModalBody = ({ onShowDeleteModal, onHide }: RevisionModal) => {
useTranslation()
const isOwner = useIsOwner()
const [selectedRevisionId, setSelectedRevisionId] = useState<number>()
const noteId = useApplicationState((state) => state.noteDetails?.id)
const [selectedRevisionId, setSelectedRevisionId] = useState<string>()
const noteAlias = useApplicationState((state) => state.noteDetails?.primaryAlias)
const {
value: revisions,
error,
loading
} = useAsync(async () => {
if (!noteId) {
if (!noteAlias) {
return []
}
return getAllRevisions(noteId)
}, [noteId])
return getAllRevisions(noteAlias)
}, [noteAlias])
const revisionLength = revisions?.length ?? 0
const enableDeleteRevisions = revisionLength > 1 && isOwner

View file

@ -15,7 +15,7 @@ import { Button, Modal } from 'react-bootstrap'
import { Trans, useTranslation } from 'react-i18next'
interface RevisionModalFooter {
selectedRevisionId?: number
selectedRevisionId?: string
disableDeleteRevisions: boolean
}
@ -37,7 +37,7 @@ export const RevisionModalFooter: React.FC<RevisionModalFooterProps> = ({
disableDeleteRevisions
}) => {
useTranslation()
const noteId = useApplicationState((state) => state.noteDetails?.id)
const noteAlias = useApplicationState((state) => state.noteDetails?.primaryAlias)
const { showErrorNotification } = useUiNotifications()
const onRevertToRevision = useCallback(() => {
@ -47,15 +47,15 @@ export const RevisionModalFooter: React.FC<RevisionModalFooterProps> = ({
}, [])
const onDownloadRevision = useCallback(() => {
if (selectedRevisionId === undefined || noteId === undefined) {
if (selectedRevisionId === undefined || noteAlias === undefined) {
return
}
getRevision(noteId, selectedRevisionId)
getRevision(noteAlias, selectedRevisionId)
.then((revision) => {
downloadRevision(noteId, revision)
downloadRevision(noteAlias, revision)
})
.catch(showErrorNotification(''))
}, [noteId, selectedRevisionId, showErrorNotification])
}, [noteAlias, selectedRevisionId, showErrorNotification])
const openDeleteModal = useCallback(() => {
onHide?.()

View file

@ -14,7 +14,7 @@ import ReactDiffViewer, { DiffMethod } from 'react-diff-viewer'
import { useAsync } from 'react-use'
export interface RevisionViewerProps {
selectedRevisionId?: number
selectedRevisionId?: string
}
/**
@ -24,7 +24,7 @@ export interface RevisionViewerProps {
* @param allRevisions List of metadata for all available revisions.
*/
export const RevisionViewer: React.FC<RevisionViewerProps> = ({ selectedRevisionId }) => {
const noteId = useApplicationState((state) => state.noteDetails?.id)
const noteAlias = useApplicationState((state) => state.noteDetails?.primaryAlias)
const darkModeEnabled = useDarkModeState()
const {
@ -32,10 +32,10 @@ export const RevisionViewer: React.FC<RevisionViewerProps> = ({ selectedRevision
error,
loading
} = useAsync(async () => {
if (noteId && selectedRevisionId !== undefined) {
return await getRevision(noteId, selectedRevisionId)
if (noteAlias && selectedRevisionId !== undefined) {
return await getRevision(noteAlias, selectedRevisionId)
}
}, [selectedRevisionId, noteId])
}, [selectedRevisionId, noteAlias])
const previousRevisionContent = useMemo(() => {
if (revision === undefined) {
@ -49,7 +49,7 @@ export const RevisionViewer: React.FC<RevisionViewerProps> = ({ selectedRevision
return applyPatch(revision.content, inversePatch) || ''
}, [revision])
if (!noteId || selectedRevisionId === undefined) {
if (!noteAlias || selectedRevisionId === undefined) {
return <Fragment />
}

View file

@ -24,16 +24,16 @@ export interface LinkFieldProps {
*/
export const NoteUrlField: React.FC<LinkFieldProps> = ({ type }) => {
const baseUrl = useBaseUrl()
const noteId = useApplicationState((state) => state.noteDetails?.id)
const noteAlias = useApplicationState((state) => state.noteDetails?.primaryAlias)
const url = useMemo(() => {
if (noteId === undefined) {
if (noteAlias === undefined) {
return null
}
const url = new URL(baseUrl)
url.pathname += `${type}/${noteId}`
url.pathname += `${type}/${noteAlias}`
return url.toString()
}, [baseUrl, noteId, type])
}, [baseUrl, noteAlias, type])
return !url ? null : <CopyableField content={url} shareOriginUrl={url} />
}

View file

@ -10,7 +10,7 @@ import { NewNoteButton } from '../../common/new-note-button/new-note-button'
import { HistoryButton } from '../../layout/app-bar/app-bar-elements/help-dropdown/history-button'
import { useFrontendConfig } from '../../common/frontend-config-context/use-frontend-config'
import { Trans, useTranslation } from 'react-i18next'
import { GuestAccess } from '@hedgedoc/commons'
import { PermissionLevel } from '@hedgedoc/commons'
/**
* Renders the card with the options for not logged-in users.
@ -20,7 +20,7 @@ export const GuestCard: React.FC = () => {
useTranslation()
if (guestAccessLevel === GuestAccess.DENY) {
if (guestAccessLevel === PermissionLevel.DENY) {
return null
}
@ -34,7 +34,7 @@ export const GuestCard: React.FC = () => {
<NewNoteButton />
<HistoryButton />
</div>
{guestAccessLevel !== GuestAccess.CREATE && (
{guestAccessLevel !== PermissionLevel.CREATE && (
<div className={'text-muted mt-2 small'}>
<Trans i18nKey={'login.guest.noteCreationDisabled'} />
</div>

View file

@ -7,7 +7,7 @@
import React, { Fragment, useMemo } from 'react'
import { useFrontendConfig } from '../../common/frontend-config-context/use-frontend-config'
import type { AuthProviderWithCustomNameDto } from '@hedgedoc/commons'
import { ProviderType } from '@hedgedoc/commons'
import { AuthProviderType } from '@hedgedoc/commons'
import { LdapLoginCard } from './ldap-login-card'
/**
@ -18,7 +18,7 @@ export const LdapLoginCards: React.FC = () => {
const ldapProviders = useMemo(() => {
return authProviders
.filter((provider) => provider.type === ProviderType.LDAP)
.filter((provider) => provider.type === AuthProviderType.LDAP)
.map((provider) => {
const ldapProvider = provider as AuthProviderWithCustomNameDto
return (

View file

@ -7,7 +7,7 @@
import React, { useMemo } from 'react'
import { Card } from 'react-bootstrap'
import { useFrontendConfig } from '../../common/frontend-config-context/use-frontend-config'
import { ProviderType } from '@hedgedoc/commons'
import { AuthProviderType } from '@hedgedoc/commons'
import { LocalLoginCardBody } from './local-login-card-body'
import { LocalRegisterCardBody } from './register/local-register-card-body'
@ -18,7 +18,7 @@ export const LocalLoginCard: React.FC = () => {
const frontendConfig = useFrontendConfig()
const localLoginEnabled = useMemo(() => {
return frontendConfig.authProviders.some((provider) => provider.type === ProviderType.LOCAL)
return frontendConfig.authProviders.some((provider) => provider.type === AuthProviderType.LOCAL)
}, [frontendConfig])
if (!localLoginEnabled) {

View file

@ -17,7 +17,7 @@ import {
} from 'react-bootstrap-icons'
import { Logger } from '../../../utils/logger'
import type { AuthProviderDto } from '@hedgedoc/commons'
import { ProviderType } from '@hedgedoc/commons'
import { AuthProviderType } from '@hedgedoc/commons'
import { IconGitlab } from '../../common/icons/additional/icon-gitlab'
import styles from './one-click-login-button.module.scss'
@ -37,7 +37,7 @@ const logger = new Logger('GetOneClickProviderMetadata')
* @return Name, icon, URL and CSS class of the given provider for rendering a login button.
*/
export const getOneClickProviderMetadata = (provider: AuthProviderDto): OneClickMetadata => {
if (provider.type !== ProviderType.OIDC) {
if (provider.type !== AuthProviderType.OIDC) {
logger.warn('Metadata for one-click-provider does not exist', provider)
return {
name: '',

View file

@ -5,7 +5,7 @@
*/
import type { AuthProviderDto } from '@hedgedoc/commons'
import { ProviderType } from '@hedgedoc/commons'
import { AuthProviderType } from '@hedgedoc/commons'
/**
* Filters the given auth providers to one-click providers only.
@ -13,5 +13,5 @@ import { ProviderType } from '@hedgedoc/commons'
* @return only one click auth providers
*/
export const filterOneClickProviders = (authProviders: AuthProviderDto[]) => {
return authProviders.filter((provider: AuthProviderDto): boolean => provider.type === ProviderType.OIDC)
return authProviders.filter((provider: AuthProviderDto): boolean => provider.type === AuthProviderType.OIDC)
}

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { FrontendConfigDto } from '@hedgedoc/commons'
import { ProviderType, GuestAccess } from '@hedgedoc/commons'
import { AuthProviderType, GuestAccess } from '@hedgedoc/commons'
import {
HttpMethod,
respondToMatchingRequest,
@ -40,16 +40,16 @@ const initialConfig: FrontendConfigDto = {
maxDocumentLength: isTestMode ? 200 : 1000000,
authProviders: [
{
type: ProviderType.LOCAL
type: AuthProviderType.LOCAL
},
{
type: ProviderType.LDAP,
type: AuthProviderType.LDAP,
identifier: 'test-ldap',
providerName: 'Test LDAP',
theme: null
},
{
type: ProviderType.OIDC,
type: AuthProviderType.OIDC,
identifier: 'test-oidc',
providerName: 'Test OIDC',
theme: null

View file

@ -6,7 +6,7 @@
import { HttpMethod, respondToMatchingRequest } from '../../../../handler-utils/respond-to-matching-request'
import type { NextApiRequest, NextApiResponse } from 'next'
import type { LoginUserInfoDto } from '@hedgedoc/commons'
import { ProviderType } from '@hedgedoc/commons'
import { AuthProviderType } from '@hedgedoc/commons'
const handler = (req: NextApiRequest, res: NextApiResponse): void => {
const cookieSet = req.headers?.['cookie']?.split(';').find((value) => value.trim() === 'mock-session=1') !== undefined
@ -18,7 +18,7 @@ const handler = (req: NextApiRequest, res: NextApiResponse): void => {
username: 'mock',
photoUrl: '/public/img/avatar.png',
displayName: 'Mock User',
authProvider: ProviderType.LOCAL,
authProvider: AuthProviderType.LOCAL,
email: 'mock@hedgedoc.test'
})
}

View file

@ -17,8 +17,8 @@ const handler = (req: NextApiRequest, res: NextApiResponse): void => {
viewCount: 0,
updatedAt: '2021-04-24T09:27:51.000Z',
createdAt: '2021-04-24T09:27:51.000Z',
updateUsername: null,
primaryAddress: 'features',
lastUpdatedBy: null,
primaryAlias: 'features',
editedBy: [],
title: 'Features',
tags: ['hedgedoc', 'demo', 'react'],

View file

@ -20,8 +20,8 @@ const handler = (req: NextApiRequest, res: NextApiResponse): void => {
viewCount: 0,
updatedAt: '2021-04-24T09:27:51.000Z',
createdAt: '2021-04-24T09:27:51.000Z',
updateUsername: null,
primaryAddress: 'features',
lastUpdatedBy: null,
primaryAlias: 'features',
editedBy: [],
title: 'New note',
tags: ['hedgedoc', 'demo', 'react'],

File diff suppressed because one or more lines are too long

View file

@ -7,7 +7,7 @@ import type { NoteDetails } from './types'
import { defaultNoteFrontmatter } from '@hedgedoc/commons'
export const initialState: NoteDetails = {
updateUsername: null,
lastUpdatedBy: null,
version: 0,
markdownContent: {
plain: '',
@ -17,17 +17,15 @@ export const initialState: NoteDetails = {
selection: { from: 0 },
rawFrontmatter: '',
startOfContentLineOffset: 0,
id: '',
createdAt: 0,
updatedAt: 0,
aliases: [],
primaryAddress: '',
primaryAlias: '',
permissions: {
owner: null,
sharedToGroups: [],
sharedToUsers: []
},
viewCount: 0,
editedBy: [],
title: '',
firstHeading: '',

View file

@ -58,7 +58,7 @@ export const updateMetadata = async (): Promise<void> => {
if (!noteDetails) {
return
}
const updatedMetadata = await getNoteMetadata(noteDetails.id)
const updatedMetadata = await getNoteMetadata(noteDetails.primaryAlias)
const action = noteDetailsActionsCreator.updateMetadata(updatedMetadata)
store.dispatch(action)
}

View file

@ -12,14 +12,14 @@ describe('build state from server permissions', () => {
it('creates a new state with the given permissions', () => {
const state: NoteDetails = { ...initialState }
const metadata: NoteMetadataDto = {
updateUsername: 'test',
lastUpdatedBy: 'test',
permissions: {
owner: null,
sharedToGroups: [],
sharedToUsers: []
},
editedBy: [],
primaryAddress: 'test-id',
primaryAlias: 'test-id',
tags: ['test'],
description: 'test',
id: 'test-id',
@ -32,14 +32,14 @@ describe('build state from server permissions', () => {
}
expect(buildStateFromMetadataUpdate(state, metadata)).toStrictEqual({
...state,
updateUsername: 'test',
lastUpdatedBy: 'test',
permissions: {
owner: null,
sharedToGroups: [],
sharedToUsers: []
},
editedBy: [],
primaryAddress: 'test-id',
primaryAlias: 'test-id',
id: 'test-id',
aliases: [],
title: 'test',

View file

@ -16,15 +16,13 @@ import type { NoteMetadataDto } from '@hedgedoc/commons'
export const buildStateFromMetadataUpdate = (state: NoteDetails, noteMetadata: NoteMetadataDto): NoteDetails => {
return {
...state,
updateUsername: noteMetadata.updateUsername,
lastUpdatedBy: noteMetadata.lastUpdatedBy,
permissions: noteMetadata.permissions,
editedBy: noteMetadata.editedBy,
primaryAddress: noteMetadata.primaryAddress,
id: noteMetadata.id,
primaryAlias: noteMetadata.primaryAlias,
aliases: noteMetadata.aliases,
title: noteMetadata.title,
version: noteMetadata.version,
viewCount: noteMetadata.viewCount,
createdAt: DateTime.fromISO(noteMetadata.createdAt).toSeconds(),
updatedAt: DateTime.fromISO(noteMetadata.updatedAt).toSeconds()
}

View file

@ -32,7 +32,7 @@ describe('build state from set note data from server', () => {
const noteDto: NoteDto = {
content: 'line1\nline2',
metadata: {
primaryAddress: 'alias',
primaryAlias: 'alias',
version: 5678,
aliases: [
{
@ -64,7 +64,7 @@ describe('build state from set note data from server', () => {
tags: ['tag'],
title: 'title',
updatedAt: '2020-05-25T09:08:34.123',
updateUsername: 'updateusername'
lastUpdatedBy: 'updateusername'
},
editedByAtPosition: [
{
@ -110,7 +110,7 @@ describe('build state from set note data from server', () => {
id: 'id',
createdAt: DateTime.fromISO('2012-05-25T09:08:34.123').toSeconds(),
updatedAt: DateTime.fromISO('2020-05-25T09:08:34.123').toSeconds(),
updateUsername: 'updateusername',
lastUpdatedBy: 'updateusername',
viewCount: 987,
aliases: [
{
@ -119,7 +119,7 @@ describe('build state from set note data from server', () => {
primaryAlias: true
}
],
primaryAddress: 'alias',
primaryAlias: 'alias',
version: 5678,
editedBy: ['editedBy'],
permissions: {

View file

@ -10,7 +10,7 @@ import { initialState as initialStateEditorConfig } from '../redux/editor-config
import { initialState as initialStateNoteDetails } from '../redux/note-details/initial-state'
import { initialState as initialStateRealtimeStatus } from '../redux/realtime/initial-state'
import { initialState as initialStateRendererStatus } from '../redux/renderer-status/initial-state'
import { type DeepPartial, ProviderType } from '@hedgedoc/commons'
import { type DeepPartial, AuthProviderType } from '@hedgedoc/commons'
jest.mock('../redux/editor-config/methods', () => ({
loadFromLocalStorage: jest.fn().mockReturnValue(undefined)
@ -54,7 +54,7 @@ export const mockAppState = (state?: DeepPartial<ApplicationState>) => {
email: state.user.email ?? null,
displayName: state.user.displayName ?? '',
photoUrl: state.user.photoUrl ?? null,
authProvider: state.user.authProvider ?? ProviderType.LOCAL
authProvider: state.user.authProvider ?? AuthProviderType.LOCAL
}
: null
})