mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-18 00:54:43 -04:00
Merge branch 'develop' into develop
This commit is contained in:
commit
e15fb2c8a3
6 changed files with 158 additions and 38 deletions
2
.idea/copyright/hedgedoc.xml
generated
2
.idea/copyright/hedgedoc.xml
generated
|
@ -1,6 +1,6 @@
|
||||||
<component name="CopyrightManager">
|
<component name="CopyrightManager">
|
||||||
<copyright>
|
<copyright>
|
||||||
<option name="notice" value="SPDX-FileCopyrightText: 2020 The HedgeDoc developers (see AUTHORS file) SPDX-License-Identifier: AGPL-3.0-only" />
|
<option name="notice" value="SPDX-FileCopyrightText: $today.year The HedgeDoc developers (see AUTHORS file) SPDX-License-Identifier: AGPL-3.0-only" />
|
||||||
<option name="myName" value="hedgedoc" />
|
<option name="myName" value="hedgedoc" />
|
||||||
</copyright>
|
</copyright>
|
||||||
</component>
|
</component>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
History of CodiMD
|
# History of HedgeDoc
|
||||||
===
|
|
||||||
|
|
||||||
## It started with HackMD
|
## It started with HackMD
|
||||||
|
|
||||||
|
@ -27,14 +26,29 @@ project), as people mistook it for an open core development model.
|
||||||
|
|
||||||
## CodiMD went independent
|
## CodiMD went independent
|
||||||
|
|
||||||
In March 2019, a discussion over licensing, governance and the future of CodiMD
|
In March 2019, a discussion over licensing, governance and the future of CodiMD lead to the formation of a distinct
|
||||||
lead to the formation of a distinct GitHub organization. Up to that point, the
|
GitHub organization. Up to that point, the community project resided in the organization of hackmdio but was for the
|
||||||
community project resided in the organization of hackmdio but was for the most
|
most part self-organized.
|
||||||
part self-organized.
|
|
||||||
|
|
||||||
During that debate, we did not reach an agreement that would have allowed us to
|
During that debate, we did not reach an agreement that would have allowed us to move the repository, so we simply forked
|
||||||
move the repository, so we simply forked it. We still welcome the HackMD team
|
it. We still welcome the HackMD team as part of our community, especially since a large portion of this code base
|
||||||
as part of our community, especially since a large portion of this code base
|
|
||||||
originated with them.
|
originated with them.
|
||||||
|
|
||||||
*For the debate that lead to this step, please refer to the [governance debate](https://github.com/hackmdio/hackmd/issues/1170) and [the announcement of the new repository](https://github.com/codimd/server/issues/10).*
|
*For the debate that lead to this step, please refer to
|
||||||
|
the [governance debate](https://github.com/hackmdio/hackmd/issues/1170)
|
||||||
|
and [the announcement of the new repository](https://github.com/hedgedoc/hedgedoc/issues/10).*
|
||||||
|
|
||||||
|
## CodiMD became HedgeDoc
|
||||||
|
|
||||||
|
With two actively named forks sharing the same name,
|
||||||
|
both [insisting on being the original/actual owner of the name CodiMD](https://github.com/hackmdio/codimd/issues/1219),
|
||||||
|
people became rightfully confused about the projects sharing the same name.
|
||||||
|
|
||||||
|
After roughly a year of being stuck on the name issue, the HedgeDoc community decided to take action and started
|
||||||
|
a [renaming process in April 2020](https://community.codimd.org/t/renaming-yet-another-time/102). With a good head start
|
||||||
|
in the amount of names, it was decided that an entire rebranding should take place and therefore, after
|
||||||
|
a [name was agreed on in July 2020](https://community.codimd.org/t/codimd-becomes-hedgedoc/170), the next step was
|
||||||
|
to [find a logo](https://community.codimd.org/t/time-to-find-the-hedgedoc-logo/171).
|
||||||
|
|
||||||
|
In November of 2020, roughly 7 months after the initiative was started, a logo was found, the rebranding of the
|
||||||
|
application as well as all community pages took place and the time of name conflicts was over. (hopefully.)
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
"rimraf": "3.0.2",
|
"rimraf": "3.0.2",
|
||||||
"rxjs": "6.6.3",
|
"rxjs": "6.6.3",
|
||||||
"shortid": "2.2.16",
|
"shortid": "2.2.16",
|
||||||
"sqlite3": "5.0.0",
|
"sqlite3": "5.0.1",
|
||||||
"swagger-ui-express": "4.1.6",
|
"swagger-ui-express": "4.1.6",
|
||||||
"typeorm": "0.2.29"
|
"typeorm": "0.2.29"
|
||||||
},
|
},
|
||||||
|
|
|
@ -10,12 +10,13 @@ import {
|
||||||
Delete,
|
Delete,
|
||||||
Get,
|
Get,
|
||||||
Header,
|
Header,
|
||||||
|
NotFoundException,
|
||||||
Param,
|
Param,
|
||||||
Post,
|
Post,
|
||||||
Put,
|
Put,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
|
import { NotInDBError } from '../../../errors/errors';
|
||||||
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||||
import { NoteMetadataUpdateDto } from '../../../notes/note-metadata.dto';
|
|
||||||
import { NotePermissionsUpdateDto } from '../../../notes/note-permissions.dto';
|
import { NotePermissionsUpdateDto } from '../../../notes/note-permissions.dto';
|
||||||
import { NotesService } from '../../../notes/notes.service';
|
import { NotesService } from '../../../notes/notes.service';
|
||||||
import { RevisionsService } from '../../../revisions/revisions.service';
|
import { RevisionsService } from '../../../revisions/revisions.service';
|
||||||
|
@ -38,8 +39,15 @@ export class NotesController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':noteIdOrAlias')
|
@Get(':noteIdOrAlias')
|
||||||
getNote(@Param('noteIdOrAlias') noteIdOrAlias: string) {
|
async getNote(@Param('noteIdOrAlias') noteIdOrAlias: string) {
|
||||||
return this.noteService.getNoteDtoByIdOrAlias(noteIdOrAlias);
|
try {
|
||||||
|
return await this.noteService.getNoteDtoByIdOrAlias(noteIdOrAlias);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof NotInDBError) {
|
||||||
|
throw new NotFoundException(e.message);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post(':noteAlias')
|
@Post(':noteAlias')
|
||||||
|
@ -54,7 +62,14 @@ export class NotesController {
|
||||||
@Delete(':noteIdOrAlias')
|
@Delete(':noteIdOrAlias')
|
||||||
async deleteNote(@Param('noteIdOrAlias') noteIdOrAlias: string) {
|
async deleteNote(@Param('noteIdOrAlias') noteIdOrAlias: string) {
|
||||||
this.logger.debug('Deleting note: ' + noteIdOrAlias);
|
this.logger.debug('Deleting note: ' + noteIdOrAlias);
|
||||||
|
try {
|
||||||
await this.noteService.deleteNoteByIdOrAlias(noteIdOrAlias);
|
await this.noteService.deleteNoteByIdOrAlias(noteIdOrAlias);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof NotInDBError) {
|
||||||
|
throw new NotFoundException(e.message);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
this.logger.debug('Successfully deleted ' + noteIdOrAlias);
|
this.logger.debug('Successfully deleted ' + noteIdOrAlias);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -65,38 +80,88 @@ export class NotesController {
|
||||||
@MarkdownBody() text: string,
|
@MarkdownBody() text: string,
|
||||||
) {
|
) {
|
||||||
this.logger.debug('Got raw markdown:\n' + text);
|
this.logger.debug('Got raw markdown:\n' + text);
|
||||||
return this.noteService.updateNoteByIdOrAlias(noteIdOrAlias, text);
|
try {
|
||||||
|
return await this.noteService.updateNoteByIdOrAlias(noteIdOrAlias, text);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof NotInDBError) {
|
||||||
|
throw new NotFoundException(e.message);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':noteIdOrAlias/content')
|
@Get(':noteIdOrAlias/content')
|
||||||
@Header('content-type', 'text/markdown')
|
@Header('content-type', 'text/markdown')
|
||||||
getNoteContent(@Param('noteIdOrAlias') noteIdOrAlias: string) {
|
async getNoteContent(@Param('noteIdOrAlias') noteIdOrAlias: string) {
|
||||||
return this.noteService.getNoteContent(noteIdOrAlias);
|
try {
|
||||||
|
return await this.noteService.getNoteContent(noteIdOrAlias);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof NotInDBError) {
|
||||||
|
throw new NotFoundException(e.message);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':noteIdOrAlias/metadata')
|
@Get(':noteIdOrAlias/metadata')
|
||||||
getNoteMetadata(@Param('noteIdOrAlias') noteIdOrAlias: string) {
|
async getNoteMetadata(@Param('noteIdOrAlias') noteIdOrAlias: string) {
|
||||||
return this.noteService.getNoteMetadata(noteIdOrAlias);
|
try {
|
||||||
|
return await this.noteService.getNoteMetadata(noteIdOrAlias);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof NotInDBError) {
|
||||||
|
throw new NotFoundException(e.message);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Put(':noteIdOrAlias/permissions')
|
@Put(':noteIdOrAlias/permissions')
|
||||||
updateNotePermissions(
|
async updateNotePermissions(
|
||||||
@Param('noteIdOrAlias') noteIdOrAlias: string,
|
@Param('noteIdOrAlias') noteIdOrAlias: string,
|
||||||
@Body() updateDto: NotePermissionsUpdateDto,
|
@Body() updateDto: NotePermissionsUpdateDto,
|
||||||
) {
|
) {
|
||||||
return this.noteService.updateNotePermissions(noteIdOrAlias, updateDto);
|
try {
|
||||||
|
return await this.noteService.updateNotePermissions(
|
||||||
|
noteIdOrAlias,
|
||||||
|
updateDto,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof NotInDBError) {
|
||||||
|
throw new NotFoundException(e.message);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':noteIdOrAlias/revisions')
|
@Get(':noteIdOrAlias/revisions')
|
||||||
getNoteRevisions(@Param('noteIdOrAlias') noteIdOrAlias: string) {
|
async getNoteRevisions(@Param('noteIdOrAlias') noteIdOrAlias: string) {
|
||||||
return this.revisionsService.getNoteRevisionMetadatas(noteIdOrAlias);
|
try {
|
||||||
|
return await this.revisionsService.getNoteRevisionMetadatas(
|
||||||
|
noteIdOrAlias,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof NotInDBError) {
|
||||||
|
throw new NotFoundException(e.message);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':noteIdOrAlias/revisions/:revisionId')
|
@Get(':noteIdOrAlias/revisions/:revisionId')
|
||||||
getNoteRevision(
|
async getNoteRevision(
|
||||||
@Param('noteIdOrAlias') noteIdOrAlias: string,
|
@Param('noteIdOrAlias') noteIdOrAlias: string,
|
||||||
@Param('revisionId') revisionId: number,
|
@Param('revisionId') revisionId: number,
|
||||||
) {
|
) {
|
||||||
return this.revisionsService.getNoteRevision(noteIdOrAlias, revisionId);
|
try {
|
||||||
|
return await this.revisionsService.getNoteRevision(
|
||||||
|
noteIdOrAlias,
|
||||||
|
revisionId,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof NotInDBError) {
|
||||||
|
throw new NotFoundException(e.message);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,12 +59,19 @@ describe('Notes', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`GET /notes/{note}`, async () => {
|
it(`GET /notes/{note}`, async () => {
|
||||||
|
// check if we can succefully get a note that exists
|
||||||
await notesService.createNote('This is a test note.', 'test1');
|
await notesService.createNote('This is a test note.', 'test1');
|
||||||
const response = await request(app.getHttpServer())
|
const response = await request(app.getHttpServer())
|
||||||
.get('/notes/test1')
|
.get('/notes/test1')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200);
|
.expect(200);
|
||||||
expect(response.body.content).toEqual('This is a test note.');
|
expect(response.body.content).toEqual('This is a test note.');
|
||||||
|
|
||||||
|
// check if a missing note correctly returns 404
|
||||||
|
await request(app.getHttpServer())
|
||||||
|
.get('/notes/i_dont_exist')
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`POST /notes/{note}`, async () => {
|
it(`POST /notes/{note}`, async () => {
|
||||||
|
@ -85,9 +92,13 @@ describe('Notes', () => {
|
||||||
it(`DELETE /notes/{note}`, async () => {
|
it(`DELETE /notes/{note}`, async () => {
|
||||||
await notesService.createNote('This is a test note.', 'test3');
|
await notesService.createNote('This is a test note.', 'test3');
|
||||||
await request(app.getHttpServer()).delete('/notes/test3').expect(200);
|
await request(app.getHttpServer()).delete('/notes/test3').expect(200);
|
||||||
return expect(notesService.getNoteByIdOrAlias('test3')).rejects.toEqual(
|
await expect(notesService.getNoteByIdOrAlias('test3')).rejects.toEqual(
|
||||||
new NotInDBError("Note with id/alias 'test3' not found."),
|
new NotInDBError("Note with id/alias 'test3' not found."),
|
||||||
);
|
);
|
||||||
|
// check if a missing note correctly returns 404
|
||||||
|
await request(app.getHttpServer())
|
||||||
|
.delete('/notes/i_dont_exist')
|
||||||
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`PUT /notes/{note}`, async () => {
|
it(`PUT /notes/{note}`, async () => {
|
||||||
|
@ -97,9 +108,16 @@ describe('Notes', () => {
|
||||||
.set('Content-Type', 'text/markdown')
|
.set('Content-Type', 'text/markdown')
|
||||||
.send('New note text')
|
.send('New note text')
|
||||||
.expect(200);
|
.expect(200);
|
||||||
return expect(
|
await expect(
|
||||||
(await notesService.getNoteDtoByIdOrAlias('test4')).content,
|
(await notesService.getNoteDtoByIdOrAlias('test4')).content,
|
||||||
).toEqual('New note text');
|
).toEqual('New note text');
|
||||||
|
|
||||||
|
// check if a missing note correctly returns 404
|
||||||
|
await request(app.getHttpServer())
|
||||||
|
.put('/notes/i_dont_exist')
|
||||||
|
.set('Content-Type', 'text/markdown')
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`GET /notes/{note}/metadata`, async () => {
|
it(`GET /notes/{note}/metadata`, async () => {
|
||||||
|
@ -124,6 +142,12 @@ describe('Notes', () => {
|
||||||
expect(typeof metadata.body.updateUser.photo).toEqual('string');
|
expect(typeof metadata.body.updateUser.photo).toEqual('string');
|
||||||
expect(typeof metadata.body.viewCount).toEqual('number');
|
expect(typeof metadata.body.viewCount).toEqual('number');
|
||||||
expect(metadata.body.editedBy).toEqual([]);
|
expect(metadata.body.editedBy).toEqual([]);
|
||||||
|
|
||||||
|
// check if a missing note correctly returns 404
|
||||||
|
await request(app.getHttpServer())
|
||||||
|
.get('/notes/i_dont_exist/metadata')
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`GET /notes/{note}/revisions`, async () => {
|
it(`GET /notes/{note}/revisions`, async () => {
|
||||||
|
@ -133,6 +157,12 @@ describe('Notes', () => {
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200);
|
.expect(200);
|
||||||
expect(response.body).toHaveLength(1);
|
expect(response.body).toHaveLength(1);
|
||||||
|
|
||||||
|
// check if a missing note correctly returns 404
|
||||||
|
await request(app.getHttpServer())
|
||||||
|
.get('/notes/i_dont_exist/revisions')
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`GET /notes/{note}/revisions/{revision-id}`, async () => {
|
it(`GET /notes/{note}/revisions/{revision-id}`, async () => {
|
||||||
|
@ -143,6 +173,12 @@ describe('Notes', () => {
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200);
|
.expect(200);
|
||||||
expect(response.body.content).toEqual('This is a test note.');
|
expect(response.body.content).toEqual('This is a test note.');
|
||||||
|
|
||||||
|
// check if a missing note correctly returns 404
|
||||||
|
await request(app.getHttpServer())
|
||||||
|
.get('/notes/i_dont_exist/revisions/1')
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`GET /notes/{note}/content`, async () => {
|
it(`GET /notes/{note}/content`, async () => {
|
||||||
|
@ -151,6 +187,11 @@ describe('Notes', () => {
|
||||||
.get('/notes/test9/content')
|
.get('/notes/test9/content')
|
||||||
.expect(200);
|
.expect(200);
|
||||||
expect(response.text).toEqual('This is a test note.');
|
expect(response.text).toEqual('This is a test note.');
|
||||||
|
|
||||||
|
// check if a missing note correctly returns 404
|
||||||
|
await request(app.getHttpServer())
|
||||||
|
.get('/notes/i_dont_exist/content')
|
||||||
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
|
|
18
yarn.lock
18
yarn.lock
|
@ -4942,10 +4942,10 @@ nice-try@^1.0.4:
|
||||||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||||
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
||||||
|
|
||||||
node-addon-api@2.0.0:
|
node-addon-api@^3.0.0:
|
||||||
version "2.0.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.0.tgz#f9afb8d777a91525244b01775ea0ddbe1125483b"
|
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.1.0.tgz#98b21931557466c6729e51cb77cd39c965f42239"
|
||||||
integrity sha512-ASCL5U13as7HhOExbT6OlWJJUV/lLzL2voOSP1UVehpRD8FbSrSDjfScK/KwAvVTI5AS6r4VwbOMlIqtvRidnA==
|
integrity sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw==
|
||||||
|
|
||||||
node-emoji@1.10.0:
|
node-emoji@1.10.0:
|
||||||
version "1.10.0"
|
version "1.10.0"
|
||||||
|
@ -6287,12 +6287,12 @@ sprintf-js@~1.0.2:
|
||||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||||
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
|
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
|
||||||
|
|
||||||
sqlite3@5.0.0:
|
sqlite3@5.0.1:
|
||||||
version "5.0.0"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-5.0.0.tgz#1bfef2151c6bc48a3ab1a6c126088bb8dd233566"
|
resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-5.0.1.tgz#d5b58c8d1568bbaf13062eb9465982f36324f78a"
|
||||||
integrity sha512-rjvqHFUaSGnzxDy2AHCwhHy6Zp6MNJzCPGYju4kD8yi6bze4d1/zMTg6C7JI49b7/EM7jKMTvyfN/4ylBKdwfw==
|
integrity sha512-kh2lTIcYNfmVcvhVJihsYuPj9U0xzBbh6bmqILO2hkryWSC9RRhzYmkIDtJkJ+d8Kg4wZRJ0T1reyHUEspICfg==
|
||||||
dependencies:
|
dependencies:
|
||||||
node-addon-api "2.0.0"
|
node-addon-api "^3.0.0"
|
||||||
node-pre-gyp "^0.11.0"
|
node-pre-gyp "^0.11.0"
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
node-gyp "3.x"
|
node-gyp "3.x"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue