feat(knex): create database interfaces and knexjs nest integration

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-06 12:59:59 +01:00
parent 902abf72e6
commit a9183e82bf
No known key found for this signature in database
GPG key ID: DB99ADDDC5C0AF82
93 changed files with 760 additions and 2927 deletions

View file

@ -1,64 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import {
Column,
Entity,
ManyToOne,
PrimaryGeneratedColumn,
Unique,
} from 'typeorm';
import { Note } from './note.entity';
import { PrimaryValueTransformer } from './primary.value-transformer';
@Entity()
@Unique('Only one primary alias per note', ['note', 'primary'])
export class Alias {
@PrimaryGeneratedColumn()
id: number;
/**
* the actual alias
*/
@Column({
nullable: false,
unique: true,
})
name: string;
/**
* Is this alias the primary alias, by which people access the note?
*/
@Column({
/*
Because of the @Unique at the top of this entity, this field must be saved as null instead of false in the DB.
If a non-primary alias would be saved with `primary: false` it would only be possible to have one non-primary and one primary alias.
But a nullable field does not have such problems.
This way the DB keeps track that one note really only has one primary alias.
*/
comment:
'This field tells you if this is the primary alias of the note. If this field is null, that means this alias is not primary.',
nullable: true,
transformer: new PrimaryValueTransformer(),
})
primary: boolean;
@ManyToOne((_) => Note, (note) => note.aliases, {
onDelete: 'CASCADE', // This deletes the Alias, when the associated Note is deleted
})
note: Promise<Note>;
// eslint-disable-next-line @typescript-eslint/no-empty-function
private constructor() {}
static create(name: string, note: Note, primary: boolean): Omit<Alias, 'id'> {
const alias = new Alias();
alias.name = name;
alias.primary = primary;
alias.note = Promise.resolve(note);
return alias;
}
}

View file

@ -17,6 +17,7 @@ import appConfigMock from '../config/mock/app.config.mock';
import authConfigMock from '../config/mock/auth.config.mock';
import databaseConfigMock from '../config/mock/database.config.mock';
import noteConfigMock from '../config/mock/note.config.mock';
import { User } from '../database/user.entity';
import {
AlreadyInDBError,
ForbiddenIdError,
@ -34,7 +35,6 @@ import { Edit } from '../revisions/edit.entity';
import { Revision } from '../revisions/revision.entity';
import { RevisionsModule } from '../revisions/revisions.module';
import { Session } from '../sessions/session.entity';
import { User } from '../users/user.entity';
import { UsersModule } from '../users/users.module';
import { mockSelectQueryBuilderInRepo } from '../utils/test-utils/mockSelectQueryBuilder';
import { Alias } from './alias.entity';

View file

@ -1,109 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import {
Column,
CreateDateColumn,
Entity,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
import { HistoryEntry } from '../history/history-entry.entity';
import { MediaUpload } from '../media/media-upload.entity';
import { NoteGroupPermission } from '../permissions/note-group-permission.entity';
import { NoteUserPermission } from '../permissions/note-user-permission.entity';
import { Revision } from '../revisions/revision.entity';
import { User } from '../users/user.entity';
import { Alias } from './alias.entity';
import { generatePublicId } from './utils';
@Entity()
export class Note {
@PrimaryGeneratedColumn()
id: number;
@Column({ type: 'text' })
publicId: string;
@OneToMany(
(_) => Alias,
(alias) => alias.note,
{ cascade: true }, // This ensures that embedded Aliases are automatically saved to the database
)
aliases: Promise<Alias[]>;
@OneToMany(
(_) => NoteGroupPermission,
(groupPermission) => groupPermission.note,
{ cascade: true }, // This ensures that embedded NoteGroupPermissions are automatically saved to the database
)
groupPermissions: Promise<NoteGroupPermission[]>;
@OneToMany(
(_) => NoteUserPermission,
(userPermission) => userPermission.note,
{ cascade: true }, // This ensures that embedded NoteUserPermission are automatically saved to the database
)
userPermissions: Promise<NoteUserPermission[]>;
@Column({
nullable: false,
default: 0,
})
viewCount: number;
@ManyToOne((_) => User, (user) => user.ownedNotes, {
onDelete: 'CASCADE', // This deletes the Note, when the associated User is deleted
nullable: true,
})
owner: Promise<User | null>;
@OneToMany((_) => Revision, (revision) => revision.note, { cascade: true })
revisions: Promise<Revision[]>;
@OneToMany((_) => HistoryEntry, (historyEntry) => historyEntry.user)
historyEntries: Promise<HistoryEntry[]>;
@OneToMany((_) => MediaUpload, (mediaUpload) => mediaUpload.note)
mediaUploads: Promise<MediaUpload[]>;
@Column({
default: 2,
})
version: number;
@CreateDateColumn()
createdAt: Date;
// eslint-disable-next-line @typescript-eslint/no-empty-function
private constructor() {}
/**
* Creates a new Note
* @param owner The owner of the note
* @param alias Optional primary alias
*/
public static create(
owner: User | null,
alias?: string,
): Omit<Note, 'id' | 'createdAt'> {
const newNote = new Note();
newNote.publicId = generatePublicId();
newNote.aliases = alias
? Promise.resolve([Alias.create(alias, newNote, true) as Alias])
: Promise.resolve([]);
newNote.userPermissions = Promise.resolve([]);
newNote.groupPermissions = Promise.resolve([]);
newNote.viewCount = 0;
newNote.owner = Promise.resolve(owner);
newNote.revisions = Promise.resolve([]);
newNote.historyEntries = Promise.resolve([]);
newNote.mediaUploads = Promise.resolve([]);
newNote.version = 2;
return newNote;
}
}

View file

@ -7,13 +7,13 @@ import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from '../database/user.entity';
import { GroupsModule } from '../groups/groups.module';
import { LoggerModule } from '../logger/logger.module';
import { NoteGroupPermission } from '../permissions/note-group-permission.entity';
import { NoteUserPermission } from '../permissions/note-user-permission.entity';
import { RealtimeNoteModule } from '../realtime/realtime-note/realtime-note.module';
import { RevisionsModule } from '../revisions/revisions.module';
import { User } from '../users/user.entity';
import { UsersModule } from '../users/users.module';
import { Alias } from './alias.entity';
import { AliasService } from './alias.service';

View file

@ -27,6 +27,7 @@ import {
registerNoteConfig,
} from '../config/mock/note.config.mock';
import { NoteConfig } from '../config/note.config';
import { User } from '../database/user.entity';
import {
AlreadyInDBError,
ForbiddenIdError,
@ -46,7 +47,6 @@ import { Revision } from '../revisions/revision.entity';
import { RevisionsModule } from '../revisions/revisions.module';
import { RevisionsService } from '../revisions/revisions.service';
import { Session } from '../sessions/session.entity';
import { User } from '../users/user.entity';
import { UsersModule } from '../users/users.module';
import { mockSelectQueryBuilderInRepo } from '../utils/test-utils/mockSelectQueryBuilder';
import { Alias } from './alias.entity';

View file

@ -16,6 +16,7 @@ import { Repository } from 'typeorm';
import { DefaultAccessLevel } from '../config/default-access-level.enum';
import noteConfiguration, { NoteConfig } from '../config/note.config';
import { User } from '../database/user.entity';
import {
AlreadyInDBError,
ForbiddenIdError,
@ -31,7 +32,6 @@ import { NoteGroupPermission } from '../permissions/note-group-permission.entity
import { RealtimeNoteStore } from '../realtime/realtime-note/realtime-note-store';
import { RealtimeNoteService } from '../realtime/realtime-note/realtime-note.service';
import { RevisionsService } from '../revisions/revisions.service';
import { User } from '../users/user.entity';
import { UsersService } from '../users/users.service';
import { Alias } from './alias.entity';
import { AliasService } from './alias.service';

View file

@ -1,22 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from 'typeorm';
import { Revision } from '../revisions/revision.entity';
@Entity()
export class Tag {
@PrimaryGeneratedColumn()
id: number;
@Column({
nullable: false,
})
name: string;
@ManyToMany((_) => Revision, (revision) => revision.tags)
revisions: Promise<Revision[]>;
}

View file

@ -5,7 +5,7 @@
*/
import { randomBytes } from 'crypto';
import { User } from '../users/user.entity';
import { User } from '../database/user.entity';
import { Alias } from './alias.entity';
import { Note } from './note.entity';
import { generatePublicId, getPrimaryAlias } from './utils';