mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-25 11:16:31 -05:00
refactor: move typeorm store into new session module
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
14ee7485ad
commit
57365bb727
16 changed files with 204 additions and 49 deletions
|
@ -96,6 +96,7 @@
|
||||||
"source-map-support": "0.5.21",
|
"source-map-support": "0.5.21",
|
||||||
"supertest": "6.2.4",
|
"supertest": "6.2.4",
|
||||||
"ts-jest": "28.0.5",
|
"ts-jest": "28.0.5",
|
||||||
|
"ts-mockery": "1.2.0",
|
||||||
"ts-node": "10.8.2",
|
"ts-node": "10.8.2",
|
||||||
"tsconfig-paths": "4.0.0",
|
"tsconfig-paths": "4.0.0",
|
||||||
"typescript": "4.7.4"
|
"typescript": "4.7.4"
|
||||||
|
|
|
@ -8,11 +8,11 @@ import { NestExpressApplication } from '@nestjs/platform-express';
|
||||||
|
|
||||||
import { AppConfig } from './config/app.config';
|
import { AppConfig } from './config/app.config';
|
||||||
import { AuthConfig } from './config/auth.config';
|
import { AuthConfig } from './config/auth.config';
|
||||||
import { DatabaseConfig } from './config/database.config';
|
|
||||||
import { MediaConfig } from './config/media.config';
|
import { MediaConfig } from './config/media.config';
|
||||||
import { ErrorExceptionMapping } from './errors/error-mapping';
|
import { ErrorExceptionMapping } from './errors/error-mapping';
|
||||||
import { ConsoleLoggerService } from './logger/console-logger.service';
|
import { ConsoleLoggerService } from './logger/console-logger.service';
|
||||||
import { BackendType } from './media/backends/backend-type.enum';
|
import { BackendType } from './media/backends/backend-type.enum';
|
||||||
|
import { SessionService } from './session/session.service';
|
||||||
import { setupSpecialGroups } from './utils/createSpecialGroups';
|
import { setupSpecialGroups } from './utils/createSpecialGroups';
|
||||||
import { setupFrontendProxy } from './utils/frontend-integration';
|
import { setupFrontendProxy } from './utils/frontend-integration';
|
||||||
import { setupSessionMiddleware } from './utils/session';
|
import { setupSessionMiddleware } from './utils/session';
|
||||||
|
@ -26,7 +26,6 @@ export async function setupApp(
|
||||||
app: NestExpressApplication,
|
app: NestExpressApplication,
|
||||||
appConfig: AppConfig,
|
appConfig: AppConfig,
|
||||||
authConfig: AuthConfig,
|
authConfig: AuthConfig,
|
||||||
databaseConfig: DatabaseConfig,
|
|
||||||
mediaConfig: MediaConfig,
|
mediaConfig: MediaConfig,
|
||||||
logger: ConsoleLoggerService,
|
logger: ConsoleLoggerService,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
@ -48,7 +47,11 @@ export async function setupApp(
|
||||||
|
|
||||||
await setupSpecialGroups(app);
|
await setupSpecialGroups(app);
|
||||||
|
|
||||||
setupSessionMiddleware(app, authConfig, databaseConfig);
|
setupSessionMiddleware(
|
||||||
|
app,
|
||||||
|
authConfig,
|
||||||
|
app.get(SessionService).getTypeormStore(),
|
||||||
|
);
|
||||||
|
|
||||||
app.enableCors({
|
app.enableCors({
|
||||||
origin: appConfig.rendererOrigin,
|
origin: appConfig.rendererOrigin,
|
||||||
|
|
|
@ -34,6 +34,7 @@ import { MonitoringModule } from './monitoring/monitoring.module';
|
||||||
import { NotesModule } from './notes/notes.module';
|
import { NotesModule } from './notes/notes.module';
|
||||||
import { PermissionsModule } from './permissions/permissions.module';
|
import { PermissionsModule } from './permissions/permissions.module';
|
||||||
import { RevisionsModule } from './revisions/revisions.module';
|
import { RevisionsModule } from './revisions/revisions.module';
|
||||||
|
import { SessionModule } from './session/session.module';
|
||||||
import { UsersModule } from './users/users.module';
|
import { UsersModule } from './users/users.module';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
|
@ -101,6 +102,7 @@ const routes: Routes = [
|
||||||
AuthModule,
|
AuthModule,
|
||||||
FrontendConfigModule,
|
FrontendConfigModule,
|
||||||
IdentityModule,
|
IdentityModule,
|
||||||
|
SessionModule,
|
||||||
],
|
],
|
||||||
controllers: [],
|
controllers: [],
|
||||||
providers: [FrontendConfigService],
|
providers: [FrontendConfigService],
|
||||||
|
|
|
@ -6,11 +6,12 @@
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { getDataSourceToken, getRepositoryToken } from '@nestjs/typeorm';
|
import { getDataSourceToken, getRepositoryToken } from '@nestjs/typeorm';
|
||||||
import { DataSource, Repository } from 'typeorm';
|
import { DataSource, EntityManager, Repository } from 'typeorm';
|
||||||
|
|
||||||
import { AuthToken } from '../auth/auth-token.entity';
|
import { AuthToken } from '../auth/auth-token.entity';
|
||||||
import { Author } from '../authors/author.entity';
|
import { Author } from '../authors/author.entity';
|
||||||
import appConfigMock from '../config/mock/app.config.mock';
|
import appConfigMock from '../config/mock/app.config.mock';
|
||||||
|
import databaseConfigMock from '../config/mock/database.config.mock';
|
||||||
import noteConfigMock from '../config/mock/note.config.mock';
|
import noteConfigMock from '../config/mock/note.config.mock';
|
||||||
import { NotInDBError } from '../errors/errors';
|
import { NotInDBError } from '../errors/errors';
|
||||||
import { Group } from '../groups/group.entity';
|
import { Group } from '../groups/group.entity';
|
||||||
|
@ -48,6 +49,16 @@ describe('HistoryService', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
noteRepo = new Repository<Note>(
|
||||||
|
'',
|
||||||
|
new EntityManager(
|
||||||
|
new DataSource({
|
||||||
|
type: 'sqlite',
|
||||||
|
database: ':memory:',
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
HistoryService,
|
HistoryService,
|
||||||
|
@ -61,7 +72,7 @@ describe('HistoryService', () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: getRepositoryToken(Note),
|
provide: getRepositoryToken(Note),
|
||||||
useClass: Repository,
|
useValue: noteRepo,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -70,7 +81,7 @@ describe('HistoryService', () => {
|
||||||
NotesModule,
|
NotesModule,
|
||||||
ConfigModule.forRoot({
|
ConfigModule.forRoot({
|
||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
load: [appConfigMock, noteConfigMock],
|
load: [appConfigMock, databaseConfigMock, noteConfigMock],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
@ -85,7 +96,7 @@ describe('HistoryService', () => {
|
||||||
.overrideProvider(getRepositoryToken(Revision))
|
.overrideProvider(getRepositoryToken(Revision))
|
||||||
.useValue({})
|
.useValue({})
|
||||||
.overrideProvider(getRepositoryToken(Note))
|
.overrideProvider(getRepositoryToken(Note))
|
||||||
.useClass(Repository)
|
.useValue(noteRepo)
|
||||||
.overrideProvider(getRepositoryToken(Tag))
|
.overrideProvider(getRepositoryToken(Tag))
|
||||||
.useValue({})
|
.useValue({})
|
||||||
.overrideProvider(getRepositoryToken(NoteGroupPermission))
|
.overrideProvider(getRepositoryToken(NoteGroupPermission))
|
||||||
|
|
14
src/main.ts
14
src/main.ts
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
@ -31,24 +31,16 @@ async function bootstrap(): Promise<void> {
|
||||||
// Initialize config and abort if we don't have a valid config
|
// Initialize config and abort if we don't have a valid config
|
||||||
const configService = app.get(ConfigService);
|
const configService = app.get(ConfigService);
|
||||||
const appConfig = configService.get<AppConfig>('appConfig');
|
const appConfig = configService.get<AppConfig>('appConfig');
|
||||||
const databaseConfig = configService.get<DatabaseConfig>('databaseConfig');
|
|
||||||
const authConfig = configService.get<AuthConfig>('authConfig');
|
const authConfig = configService.get<AuthConfig>('authConfig');
|
||||||
const mediaConfig = configService.get<MediaConfig>('mediaConfig');
|
const mediaConfig = configService.get<MediaConfig>('mediaConfig');
|
||||||
if (!appConfig || !databaseConfig || !authConfig || !mediaConfig) {
|
if (!appConfig || !authConfig || !mediaConfig) {
|
||||||
logger.error('Could not initialize config, aborting.', 'AppBootstrap');
|
logger.error('Could not initialize config, aborting.', 'AppBootstrap');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call common setup function which handles the rest
|
// Call common setup function which handles the rest
|
||||||
// Setup code must be added there!
|
// Setup code must be added there!
|
||||||
await setupApp(
|
await setupApp(app, appConfig, authConfig, mediaConfig, logger);
|
||||||
app,
|
|
||||||
appConfig,
|
|
||||||
authConfig,
|
|
||||||
databaseConfig,
|
|
||||||
mediaConfig,
|
|
||||||
logger,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Start the server
|
// Start the server
|
||||||
await app.listen(appConfig.port);
|
await app.listen(appConfig.port);
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { Repository } from 'typeorm';
|
||||||
import appConfigMock from '../../src/config/mock/app.config.mock';
|
import appConfigMock from '../../src/config/mock/app.config.mock';
|
||||||
import { AuthToken } from '../auth/auth-token.entity';
|
import { AuthToken } from '../auth/auth-token.entity';
|
||||||
import { Author } from '../authors/author.entity';
|
import { Author } from '../authors/author.entity';
|
||||||
|
import databaseConfigMock from '../config/mock/database.config.mock';
|
||||||
import mediaConfigMock from '../config/mock/media.config.mock';
|
import mediaConfigMock from '../config/mock/media.config.mock';
|
||||||
import noteConfigMock from '../config/mock/note.config.mock';
|
import noteConfigMock from '../config/mock/note.config.mock';
|
||||||
import { ClientError, NotInDBError } from '../errors/errors';
|
import { ClientError, NotInDBError } from '../errors/errors';
|
||||||
|
@ -52,7 +53,12 @@ describe('MediaService', () => {
|
||||||
imports: [
|
imports: [
|
||||||
ConfigModule.forRoot({
|
ConfigModule.forRoot({
|
||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
load: [mediaConfigMock, appConfigMock, noteConfigMock],
|
load: [
|
||||||
|
mediaConfigMock,
|
||||||
|
appConfigMock,
|
||||||
|
databaseConfigMock,
|
||||||
|
noteConfigMock,
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
LoggerModule,
|
LoggerModule,
|
||||||
NotesModule,
|
NotesModule,
|
||||||
|
|
|
@ -6,11 +6,12 @@
|
||||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||||
import { Repository } from 'typeorm';
|
import { DataSource, EntityManager, Repository } from 'typeorm';
|
||||||
|
|
||||||
import { AuthToken } from '../auth/auth-token.entity';
|
import { AuthToken } from '../auth/auth-token.entity';
|
||||||
import { Author } from '../authors/author.entity';
|
import { Author } from '../authors/author.entity';
|
||||||
import appConfigMock from '../config/mock/app.config.mock';
|
import appConfigMock from '../config/mock/app.config.mock';
|
||||||
|
import databaseConfigMock from '../config/mock/database.config.mock';
|
||||||
import noteConfigMock from '../config/mock/note.config.mock';
|
import noteConfigMock from '../config/mock/note.config.mock';
|
||||||
import {
|
import {
|
||||||
AlreadyInDBError,
|
AlreadyInDBError,
|
||||||
|
@ -43,13 +44,23 @@ describe('AliasService', () => {
|
||||||
let aliasRepo: Repository<Alias>;
|
let aliasRepo: Repository<Alias>;
|
||||||
let forbiddenNoteId: string;
|
let forbiddenNoteId: string;
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
noteRepo = new Repository<Note>(
|
||||||
|
'',
|
||||||
|
new EntityManager(
|
||||||
|
new DataSource({
|
||||||
|
type: 'sqlite',
|
||||||
|
database: ':memory:',
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
AliasService,
|
AliasService,
|
||||||
NotesService,
|
NotesService,
|
||||||
{
|
{
|
||||||
provide: getRepositoryToken(Note),
|
provide: getRepositoryToken(Note),
|
||||||
useClass: Repository,
|
useValue: noteRepo,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: getRepositoryToken(Alias),
|
provide: getRepositoryToken(Alias),
|
||||||
|
@ -67,7 +78,7 @@ describe('AliasService', () => {
|
||||||
imports: [
|
imports: [
|
||||||
ConfigModule.forRoot({
|
ConfigModule.forRoot({
|
||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
load: [appConfigMock, noteConfigMock],
|
load: [appConfigMock, databaseConfigMock, noteConfigMock],
|
||||||
}),
|
}),
|
||||||
LoggerModule,
|
LoggerModule,
|
||||||
UsersModule,
|
UsersModule,
|
||||||
|
@ -77,7 +88,7 @@ describe('AliasService', () => {
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
.overrideProvider(getRepositoryToken(Note))
|
.overrideProvider(getRepositoryToken(Note))
|
||||||
.useClass(Repository)
|
.useValue(noteRepo)
|
||||||
.overrideProvider(getRepositoryToken(Tag))
|
.overrideProvider(getRepositoryToken(Tag))
|
||||||
.useClass(Repository)
|
.useClass(Repository)
|
||||||
.overrideProvider(getRepositoryToken(Alias))
|
.overrideProvider(getRepositoryToken(Alias))
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { DataSource, EntityManager, Repository } from 'typeorm';
|
||||||
import { AuthToken } from '../auth/auth-token.entity';
|
import { AuthToken } from '../auth/auth-token.entity';
|
||||||
import { Author } from '../authors/author.entity';
|
import { Author } from '../authors/author.entity';
|
||||||
import appConfigMock from '../config/mock/app.config.mock';
|
import appConfigMock from '../config/mock/app.config.mock';
|
||||||
|
import databaseConfigMock from '../config/mock/database.config.mock';
|
||||||
import noteConfigMock from '../config/mock/note.config.mock';
|
import noteConfigMock from '../config/mock/note.config.mock';
|
||||||
import {
|
import {
|
||||||
AlreadyInDBError,
|
AlreadyInDBError,
|
||||||
|
@ -135,13 +136,23 @@ describe('NotesService', () => {
|
||||||
),
|
),
|
||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
|
noteRepo = new Repository<Note>(
|
||||||
|
'',
|
||||||
|
new EntityManager(
|
||||||
|
new DataSource({
|
||||||
|
type: 'sqlite',
|
||||||
|
database: ':memory:',
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
NotesService,
|
NotesService,
|
||||||
AliasService,
|
AliasService,
|
||||||
{
|
{
|
||||||
provide: getRepositoryToken(Note),
|
provide: getRepositoryToken(Note),
|
||||||
useClass: Repository,
|
useValue: noteRepo,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: getRepositoryToken(Tag),
|
provide: getRepositoryToken(Tag),
|
||||||
|
@ -157,18 +168,18 @@ describe('NotesService', () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
ConfigModule.forRoot({
|
|
||||||
isGlobal: true,
|
|
||||||
load: [appConfigMock, noteConfigMock],
|
|
||||||
}),
|
|
||||||
LoggerModule,
|
LoggerModule,
|
||||||
UsersModule,
|
UsersModule,
|
||||||
GroupsModule,
|
GroupsModule,
|
||||||
RevisionsModule,
|
RevisionsModule,
|
||||||
|
ConfigModule.forRoot({
|
||||||
|
isGlobal: true,
|
||||||
|
load: [appConfigMock, databaseConfigMock, noteConfigMock],
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
.overrideProvider(getRepositoryToken(Note))
|
.overrideProvider(getRepositoryToken(Note))
|
||||||
.useClass(Repository)
|
.useValue(noteRepo)
|
||||||
.overrideProvider(getRepositoryToken(Tag))
|
.overrideProvider(getRepositoryToken(Tag))
|
||||||
.useClass(Repository)
|
.useClass(Repository)
|
||||||
.overrideProvider(getRepositoryToken(Alias))
|
.overrideProvider(getRepositoryToken(Alias))
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { DataSource, EntityManager, Repository } from 'typeorm';
|
||||||
import { AuthToken } from '../auth/auth-token.entity';
|
import { AuthToken } from '../auth/auth-token.entity';
|
||||||
import { Author } from '../authors/author.entity';
|
import { Author } from '../authors/author.entity';
|
||||||
import appConfigMock from '../config/mock/app.config.mock';
|
import appConfigMock from '../config/mock/app.config.mock';
|
||||||
|
import databaseConfigMock from '../config/mock/database.config.mock';
|
||||||
import noteConfigMock from '../config/mock/note.config.mock';
|
import noteConfigMock from '../config/mock/note.config.mock';
|
||||||
import { PermissionsUpdateInconsistentError } from '../errors/errors';
|
import { PermissionsUpdateInconsistentError } from '../errors/errors';
|
||||||
import { Group } from '../groups/group.entity';
|
import { Group } from '../groups/group.entity';
|
||||||
|
@ -86,17 +87,13 @@ describe('PermissionsService', () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
ConfigModule.forRoot({
|
|
||||||
isGlobal: true,
|
|
||||||
load: [appConfigMock],
|
|
||||||
}),
|
|
||||||
LoggerModule,
|
LoggerModule,
|
||||||
PermissionsModule,
|
PermissionsModule,
|
||||||
UsersModule,
|
UsersModule,
|
||||||
NotesModule,
|
NotesModule,
|
||||||
ConfigModule.forRoot({
|
ConfigModule.forRoot({
|
||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
load: [appConfigMock, noteConfigMock],
|
load: [appConfigMock, databaseConfigMock, noteConfigMock],
|
||||||
}),
|
}),
|
||||||
GroupsModule,
|
GroupsModule,
|
||||||
],
|
],
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { Repository } from 'typeorm';
|
||||||
import { AuthToken } from '../auth/auth-token.entity';
|
import { AuthToken } from '../auth/auth-token.entity';
|
||||||
import { Author } from '../authors/author.entity';
|
import { Author } from '../authors/author.entity';
|
||||||
import appConfigMock from '../config/mock/app.config.mock';
|
import appConfigMock from '../config/mock/app.config.mock';
|
||||||
|
import databaseConfigMock from '../config/mock/database.config.mock';
|
||||||
import noteConfigMock from '../config/mock/note.config.mock';
|
import noteConfigMock from '../config/mock/note.config.mock';
|
||||||
import { NotInDBError } from '../errors/errors';
|
import { NotInDBError } from '../errors/errors';
|
||||||
import { Group } from '../groups/group.entity';
|
import { Group } from '../groups/group.entity';
|
||||||
|
@ -48,7 +49,7 @@ describe('RevisionsService', () => {
|
||||||
LoggerModule,
|
LoggerModule,
|
||||||
ConfigModule.forRoot({
|
ConfigModule.forRoot({
|
||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
load: [appConfigMock, noteConfigMock],
|
load: [appConfigMock, databaseConfigMock, noteConfigMock],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
17
src/session/session.module.ts
Normal file
17
src/session/session.module.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { Session } from '../users/session.entity';
|
||||||
|
import { SessionService } from './session.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [TypeOrmModule.forFeature([Session])],
|
||||||
|
exports: [SessionService],
|
||||||
|
providers: [SessionService],
|
||||||
|
})
|
||||||
|
export class SessionModule {}
|
55
src/session/session.service.spec.ts
Normal file
55
src/session/session.service.spec.ts
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import * as ConnectTypeormModule from 'connect-typeorm';
|
||||||
|
import { TypeormStore } from 'connect-typeorm';
|
||||||
|
import { Mock } from 'ts-mockery';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
|
import { DatabaseType } from '../config/database-type.enum';
|
||||||
|
import { DatabaseConfig } from '../config/database.config';
|
||||||
|
import { Session } from '../users/session.entity';
|
||||||
|
import { SessionService } from './session.service';
|
||||||
|
|
||||||
|
jest.mock('cookie');
|
||||||
|
jest.mock('cookie-signature');
|
||||||
|
|
||||||
|
describe('SessionService', () => {
|
||||||
|
let mockedTypeormStore: TypeormStore;
|
||||||
|
let mockedSessionRepository: Repository<Session>;
|
||||||
|
let databaseConfigMock: DatabaseConfig;
|
||||||
|
let typeormStoreConstructorMock: jest.SpyInstance;
|
||||||
|
let sessionService: SessionService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.resetModules();
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
mockedTypeormStore = Mock.of<TypeormStore>({
|
||||||
|
connect: jest.fn(() => mockedTypeormStore),
|
||||||
|
});
|
||||||
|
mockedSessionRepository = Mock.of<Repository<Session>>({});
|
||||||
|
databaseConfigMock = Mock.of<DatabaseConfig>({
|
||||||
|
type: DatabaseType.SQLITE,
|
||||||
|
});
|
||||||
|
|
||||||
|
typeormStoreConstructorMock = jest
|
||||||
|
.spyOn(ConnectTypeormModule, 'TypeormStore')
|
||||||
|
.mockReturnValue(mockedTypeormStore);
|
||||||
|
|
||||||
|
sessionService = new SessionService(
|
||||||
|
mockedSessionRepository,
|
||||||
|
databaseConfigMock,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a new TypeormStore on create', () => {
|
||||||
|
expect(typeormStoreConstructorMock).toBeCalledWith({
|
||||||
|
cleanupLimit: 2,
|
||||||
|
limitSubquery: true,
|
||||||
|
});
|
||||||
|
expect(mockedTypeormStore.connect).toBeCalledWith(mockedSessionRepository);
|
||||||
|
expect(sessionService.getTypeormStore()).toBe(mockedTypeormStore);
|
||||||
|
});
|
||||||
|
});
|
35
src/session/session.service.ts
Normal file
35
src/session/session.service.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { TypeormStore } from 'connect-typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
|
import { DatabaseType } from '../config/database-type.enum';
|
||||||
|
import databaseConfiguration, {
|
||||||
|
DatabaseConfig,
|
||||||
|
} from '../config/database.config';
|
||||||
|
import { Session } from '../users/session.entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class SessionService {
|
||||||
|
private readonly typeormStore: TypeormStore;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(Session) private sessionRepository: Repository<Session>,
|
||||||
|
@Inject(databaseConfiguration.KEY)
|
||||||
|
private dbConfig: DatabaseConfig,
|
||||||
|
) {
|
||||||
|
this.typeormStore = new TypeormStore({
|
||||||
|
cleanupLimit: 2,
|
||||||
|
limitSubquery: dbConfig.type !== DatabaseType.MARIADB,
|
||||||
|
}).connect(sessionRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTypeormStore(): TypeormStore {
|
||||||
|
return this.typeormStore;
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,40 +4,34 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import { INestApplication } from '@nestjs/common';
|
import { INestApplication } from '@nestjs/common';
|
||||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
|
||||||
import { TypeormStore } from 'connect-typeorm';
|
import { TypeormStore } from 'connect-typeorm';
|
||||||
import session from 'express-session';
|
import session from 'express-session';
|
||||||
import { Repository } from 'typeorm';
|
|
||||||
|
|
||||||
import { AuthConfig } from '../config/auth.config';
|
import { AuthConfig } from '../config/auth.config';
|
||||||
import { DatabaseType } from '../config/database-type.enum';
|
|
||||||
import { DatabaseConfig } from '../config/database.config';
|
export const HEDGEDOC_SESSION = 'hedgedoc-session';
|
||||||
import { Session } from '../users/session.entity';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup the session middleware via the given authConfig.
|
* Set up the session middleware via the given authConfig.
|
||||||
* @param {INestApplication} app - the nest application to configure the middleware for.
|
* @param {INestApplication} app - the nest application to configure the middleware for.
|
||||||
* @param {AuthConfig} authConfig - the authConfig to configure the middleware with.
|
* @param {AuthConfig} authConfig - the authConfig to configure the middleware with.
|
||||||
* @param {DatabaseConfig} dbConfig - the DatabaseConfig to configure the middleware with.
|
* @param {TypeormStore} typeormStore - the typeormStore to handle session data.
|
||||||
*/
|
*/
|
||||||
export function setupSessionMiddleware(
|
export function setupSessionMiddleware(
|
||||||
app: INestApplication,
|
app: INestApplication,
|
||||||
authConfig: AuthConfig,
|
authConfig: AuthConfig,
|
||||||
dbConfig: DatabaseConfig,
|
typeormStore: TypeormStore,
|
||||||
): void {
|
): void {
|
||||||
app.use(
|
app.use(
|
||||||
session({
|
session({
|
||||||
name: 'hedgedoc-session',
|
name: HEDGEDOC_SESSION,
|
||||||
secret: authConfig.session.secret,
|
secret: authConfig.session.secret,
|
||||||
cookie: {
|
cookie: {
|
||||||
maxAge: authConfig.session.lifetime,
|
maxAge: authConfig.session.lifetime,
|
||||||
},
|
},
|
||||||
resave: false,
|
resave: false,
|
||||||
saveUninitialized: false,
|
saveUninitialized: false,
|
||||||
store: new TypeormStore({
|
store: typeormStore,
|
||||||
cleanupLimit: 2,
|
|
||||||
limitSubquery: dbConfig.type !== DatabaseType.MARIADB,
|
|
||||||
}).connect(app.get<Repository<Session>>(getRepositoryToken(Session))),
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,9 @@ import { NotesService } from '../src/notes/notes.service';
|
||||||
import { PermissionsModule } from '../src/permissions/permissions.module';
|
import { PermissionsModule } from '../src/permissions/permissions.module';
|
||||||
import { PermissionsService } from '../src/permissions/permissions.service';
|
import { PermissionsService } from '../src/permissions/permissions.service';
|
||||||
import { RevisionsModule } from '../src/revisions/revisions.module';
|
import { RevisionsModule } from '../src/revisions/revisions.module';
|
||||||
|
import { RevisionsService } from '../src/revisions/revisions.service';
|
||||||
|
import { SessionModule } from '../src/session/session.module';
|
||||||
|
import { SessionService } from '../src/session/session.service';
|
||||||
import { User } from '../src/users/user.entity';
|
import { User } from '../src/users/user.entity';
|
||||||
import { UsersModule } from '../src/users/users.module';
|
import { UsersModule } from '../src/users/users.module';
|
||||||
import { UsersService } from '../src/users/users.service';
|
import { UsersService } from '../src/users/users.service';
|
||||||
|
@ -67,6 +70,8 @@ export class TestSetup {
|
||||||
historyService: HistoryService;
|
historyService: HistoryService;
|
||||||
aliasService: AliasService;
|
aliasService: AliasService;
|
||||||
authService: AuthService;
|
authService: AuthService;
|
||||||
|
sessionService: SessionService;
|
||||||
|
revisionsService: RevisionsService;
|
||||||
|
|
||||||
users: User[] = [];
|
users: User[] = [];
|
||||||
authTokens: AuthTokenWithSecretDto[] = [];
|
authTokens: AuthTokenWithSecretDto[] = [];
|
||||||
|
@ -226,6 +231,7 @@ export class TestSetupBuilder {
|
||||||
AuthModule,
|
AuthModule,
|
||||||
FrontendConfigModule,
|
FrontendConfigModule,
|
||||||
IdentityModule,
|
IdentityModule,
|
||||||
|
SessionModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
|
@ -269,6 +275,10 @@ export class TestSetupBuilder {
|
||||||
this.testSetup.moduleRef.get<AuthService>(AuthService);
|
this.testSetup.moduleRef.get<AuthService>(AuthService);
|
||||||
this.testSetup.permissionsService =
|
this.testSetup.permissionsService =
|
||||||
this.testSetup.moduleRef.get<PermissionsService>(PermissionsService);
|
this.testSetup.moduleRef.get<PermissionsService>(PermissionsService);
|
||||||
|
this.testSetup.sessionService =
|
||||||
|
this.testSetup.moduleRef.get<SessionService>(SessionService);
|
||||||
|
this.testSetup.revisionsService =
|
||||||
|
this.testSetup.moduleRef.get<RevisionsService>(RevisionsService);
|
||||||
|
|
||||||
this.testSetup.app = this.testSetup.moduleRef.createNestApplication();
|
this.testSetup.app = this.testSetup.moduleRef.createNestApplication();
|
||||||
|
|
||||||
|
@ -276,7 +286,6 @@ export class TestSetupBuilder {
|
||||||
this.testSetup.app,
|
this.testSetup.app,
|
||||||
this.testSetup.configService.get<AppConfig>('appConfig'),
|
this.testSetup.configService.get<AppConfig>('appConfig'),
|
||||||
this.testSetup.configService.get<AuthConfig>('authConfig'),
|
this.testSetup.configService.get<AuthConfig>('authConfig'),
|
||||||
this.testSetup.configService.get<DatabaseConfig>('databaseConfig'),
|
|
||||||
this.testSetup.configService.get<MediaConfig>('mediaConfig'),
|
this.testSetup.configService.get<MediaConfig>('mediaConfig'),
|
||||||
await this.testSetup.app.resolve(ConsoleLoggerService),
|
await this.testSetup.app.resolve(ConsoleLoggerService),
|
||||||
);
|
);
|
||||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -5325,6 +5325,7 @@ __metadata:
|
||||||
supertest: 6.2.4
|
supertest: 6.2.4
|
||||||
swagger-ui-express: 4.4.0
|
swagger-ui-express: 4.4.0
|
||||||
ts-jest: 28.0.5
|
ts-jest: 28.0.5
|
||||||
|
ts-mockery: ^1.2.0
|
||||||
ts-node: 10.8.2
|
ts-node: 10.8.2
|
||||||
tsconfig-paths: 4.0.0
|
tsconfig-paths: 4.0.0
|
||||||
typeorm: 0.3.7
|
typeorm: 0.3.7
|
||||||
|
@ -9176,6 +9177,15 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"ts-mockery@npm:^1.2.0":
|
||||||
|
version: 1.2.0
|
||||||
|
resolution: "ts-mockery@npm:1.2.0"
|
||||||
|
peerDependencies:
|
||||||
|
typescript: ">= 2.8"
|
||||||
|
checksum: 01c5b8cbbc2b716ed96acbcc78679a27cc787b575a76fed7eb1beaa8deed8da274e091a9e7dcc77941e290f46481307b550b6e9799ba631410c5173a7be3f442
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"ts-node@npm:10.8.2":
|
"ts-node@npm:10.8.2":
|
||||||
version: 10.8.2
|
version: 10.8.2
|
||||||
resolution: "ts-node@npm:10.8.2"
|
resolution: "ts-node@npm:10.8.2"
|
||||||
|
|
Loading…
Reference in a new issue