diff --git a/package.json b/package.json index 07079d4de..62043b0d2 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "HedgeDoc", + "name": "hedgedoc", "version": "2.0.0", "description": "Realtime collaborative markdown notes on all platforms.", "author": "", @@ -30,6 +30,7 @@ "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^6.5.4", + "shortid": "^2.2.15", "sqlite3": "^5.0.0", "typeorm": "^0.2.25" }, diff --git a/src/notes/note-metadata.dto.ts b/src/notes/note-metadata.dto.ts new file mode 100644 index 000000000..b68d0aef9 --- /dev/null +++ b/src/notes/note-metadata.dto.ts @@ -0,0 +1,36 @@ +import { + IsArray, + IsDate, + IsNumber, + IsString, + ValidateNested, +} from 'class-validator'; +import { UserInfoDto } from '../users/user-info.dto'; +import { NotePermissionsDto } from './note-permissions.dto'; + +export class NoteMetadataDto { + @IsString() + id: string; + @IsString() + alias: string; + @IsString() + title: string; + @IsString() + description: string; + @IsArray() + @IsString({ each: true }) + tags: string[]; + @IsDate() + updateTime: Date; + @ValidateNested() + updateUser: UserInfoDto; + @IsNumber() + viewCount: number; + @IsDate() + createTime: Date; + @IsArray() + @ValidateNested() + editedBy: UserInfoDto['userName'][]; + @ValidateNested() + permission: NotePermissionsDto; +} diff --git a/src/notes/note-permissions.dto.ts b/src/notes/note-permissions.dto.ts new file mode 100644 index 000000000..8e90f77dd --- /dev/null +++ b/src/notes/note-permissions.dto.ts @@ -0,0 +1,17 @@ +import { IsArray, IsBoolean, ValidateNested } from 'class-validator'; +import { UserInfoDto } from '../users/user-info.dto'; + +export class NotePermissionEntryDto { + @ValidateNested() + user: UserInfoDto; + @IsBoolean() + canEdit: boolean; +} + +export class NotePermissionsDto { + @ValidateNested() + owner: UserInfoDto; + @ValidateNested() + @IsArray() + sharedTo: NotePermissionEntryDto[]; +} diff --git a/src/notes/note.entity.ts b/src/notes/note.entity.ts new file mode 100644 index 000000000..0b3a7fa1b --- /dev/null +++ b/src/notes/note.entity.ts @@ -0,0 +1,120 @@ +import { generate as shortIdGenerate } from 'shortid'; +import { + Column, + Entity, + ManyToOne, + OneToMany, + PrimaryGeneratedColumn, +} from 'typeorm'; +import { Author } from '../authors/author.entity'; +import { Revision } from '../revisions/revision.entity'; +import { User } from '../users/user.entity'; + +// permission types +enum PermissionEnum { + freely = 'freely', + editable = 'editable', + limited = 'limited', + locked = 'locked', + protected = 'protected', + private = 'private', +} + +@Entity('Notes') +export class Note { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ + nullable: false, + unique: true, + }) + shortid: string; + + @Column({ + unique: true, + nullable: true, + }) + alias: string; + + @Column({ + type: 'text', + }) + permission: PermissionEnum; + + @Column({ + nullable: false, + default: 0, + }) + viewcount: number; + + @Column({ + nullable: true, + }) + lastchangeAt: Date; + + @Column() + savedAt: Date; + + @ManyToOne(_ => User, { onDelete: 'CASCADE' }) + owner: User; + + @ManyToOne(_ => User) + lastchangeuser: User; + + @OneToMany( + _ => Revision, + revision => revision.note, + ) + revisions: Revision[]; + + @OneToMany( + _ => Author, + author => author.note, + ) + authors: Author[]; + + @Column({ + type: 'text', + nullable: true, + }) + title: string; + + @Column({ + type: 'text', + }) + content: string; + + @Column({ + type: 'text', + nullable: true, + }) + authorship: string; + + constructor( + shortid: string, + alias: string, + permission: PermissionEnum, + lastchangeAt: Date, + savedAt: Date, + owner: User, + title: string, + content: string, + authorship: string, + ) { + if (shortid) { + this.shortid = shortid; + } else { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + this.shortid = shortIdGenerate() as string; + } + this.alias = alias; + this.permission = permission; + this.lastchangeAt = lastchangeAt; + this.savedAt = savedAt; + this.owner = owner; + this.title = title; + this.content = content; + this.authorship = authorship; + } +} diff --git a/src/notes/notes.module.ts b/src/notes/notes.module.ts new file mode 100644 index 000000000..b267951f9 --- /dev/null +++ b/src/notes/notes.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { Note } from './note.entity'; + +@Module({ + imports: [TypeOrmModule.forFeature([Note])], + controllers: [], +}) +export class NotesModule {} diff --git a/yarn.lock b/yarn.lock index afab1a992..b5328ac03 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4983,6 +4983,11 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== +nanoid@^2.1.0: + version "2.1.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280" + integrity sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -6339,6 +6344,13 @@ shellwords@^0.1.1: resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== +shortid@^2.2.15: + version "2.2.15" + resolved "https://registry.yarnpkg.com/shortid/-/shortid-2.2.15.tgz#2b902eaa93a69b11120373cd42a1f1fe4437c122" + integrity sha512-5EaCy2mx2Jgc/Fdn9uuDuNIIfWBpzY4XIlhoqtXF6qsf+/+SGZ+FxDdX/ZsMZiWupIWNqAEmiNY4RC+LSmCeOw== + dependencies: + nanoid "^2.1.0" + signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"