mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-09 05:41:57 -04:00
refactor(backend): use @hedgedoc/commons DTOs
Co-authored-by: Erik Michelson <github@erik.michelson.eu> Signed-off-by: Erik Michelson <github@erik.michelson.eu> Signed-off-by: Philip Molares <philip.molares@udo.edu>
This commit is contained in:
parent
7285c2bc50
commit
b11dbd51c8
94 changed files with 514 additions and 1642 deletions
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsDate, IsNumber, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
import { BaseDto } from '../utils/base.dto.';
|
||||
import { TimestampMillis } from '../utils/timestamp';
|
||||
|
||||
export class ApiTokenDto extends BaseDto {
|
||||
@IsString()
|
||||
label: string;
|
||||
|
||||
@IsString()
|
||||
keyId: string;
|
||||
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
createdAt: Date;
|
||||
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
validUntil: Date;
|
||||
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
@IsOptional()
|
||||
lastUsedAt: Date | null;
|
||||
}
|
||||
|
||||
export class ApiTokenWithSecretDto extends ApiTokenDto {
|
||||
@IsString()
|
||||
secret: string;
|
||||
}
|
||||
|
||||
export class ApiTokenCreateDto extends BaseDto {
|
||||
@IsString()
|
||||
label: string;
|
||||
|
||||
@IsNumber()
|
||||
@Type(() => Number)
|
||||
validUntil: TimestampMillis;
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { DeepPartial } from '@hedgedoc/commons';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||
|
@ -150,7 +149,7 @@ describe('ApiTokenService', () => {
|
|||
const [accessToken, secret] = service.createToken(
|
||||
user,
|
||||
'TestToken',
|
||||
undefined,
|
||||
null,
|
||||
);
|
||||
|
||||
expect(() =>
|
||||
|
@ -158,7 +157,7 @@ describe('ApiTokenService', () => {
|
|||
).not.toThrow();
|
||||
});
|
||||
it('AuthToken has wrong hash', () => {
|
||||
const [accessToken] = service.createToken(user, 'TestToken', undefined);
|
||||
const [accessToken] = service.createToken(user, 'TestToken', null);
|
||||
expect(() =>
|
||||
service.checkToken('secret', accessToken as ApiToken),
|
||||
).toThrow(TokenNotValidError);
|
||||
|
@ -167,7 +166,7 @@ describe('ApiTokenService', () => {
|
|||
const [accessToken, secret] = service.createToken(
|
||||
user,
|
||||
'Test',
|
||||
1549312452000,
|
||||
new Date(1549312452000),
|
||||
);
|
||||
expect(() => service.checkToken(secret, accessToken as ApiToken)).toThrow(
|
||||
TokenNotValidError,
|
||||
|
@ -295,18 +294,16 @@ describe('ApiTokenService', () => {
|
|||
jest
|
||||
.spyOn(apiTokenRepo, 'save')
|
||||
.mockImplementationOnce(
|
||||
async (
|
||||
apiTokenSaved: DeepPartial<ApiToken>,
|
||||
_,
|
||||
): Promise<ApiToken> => {
|
||||
async (apiTokenSaved: ApiToken, _): Promise<ApiToken> => {
|
||||
expect(apiTokenSaved.lastUsedAt).toBeNull();
|
||||
apiTokenSaved.createdAt = new Date(1);
|
||||
return apiTokenSaved;
|
||||
},
|
||||
);
|
||||
const token = await service.addToken(user, identifier, 0);
|
||||
const token = await service.addToken(user, identifier, new Date(0));
|
||||
expect(token.label).toEqual(identifier);
|
||||
expect(
|
||||
token.validUntil.getTime() -
|
||||
new Date(token.validUntil).getTime() -
|
||||
(new Date().getTime() + 2 * 365 * 24 * 60 * 60 * 1000),
|
||||
).toBeLessThanOrEqual(10000);
|
||||
expect(token.lastUsedAt).toBeNull();
|
||||
|
@ -317,18 +314,17 @@ describe('ApiTokenService', () => {
|
|||
jest
|
||||
.spyOn(apiTokenRepo, 'save')
|
||||
.mockImplementationOnce(
|
||||
async (
|
||||
apiTokenSaved: DeepPartial<ApiToken>,
|
||||
_,
|
||||
): Promise<ApiToken> => {
|
||||
async (apiTokenSaved: ApiToken, _): Promise<ApiToken> => {
|
||||
expect(apiTokenSaved.lastUsedAt).toBeNull();
|
||||
apiTokenSaved.createdAt = new Date(1);
|
||||
return apiTokenSaved;
|
||||
},
|
||||
);
|
||||
const validUntil = new Date().getTime() + 30000;
|
||||
const validUntil = new Date();
|
||||
validUntil.setTime(validUntil.getTime() + 30000);
|
||||
const token = await service.addToken(user, identifier, validUntil);
|
||||
expect(token.label).toEqual(identifier);
|
||||
expect(token.validUntil.getTime()).toEqual(validUntil);
|
||||
expect(new Date(token.validUntil)).toEqual(validUntil);
|
||||
expect(token.lastUsedAt).toBeNull();
|
||||
expect(token.secret.startsWith('hd2.' + token.keyId)).toBeTruthy();
|
||||
});
|
||||
|
@ -340,7 +336,8 @@ describe('ApiTokenService', () => {
|
|||
inValidToken.length = 201;
|
||||
return inValidToken;
|
||||
});
|
||||
const validUntil = new Date().getTime() + 30000;
|
||||
const validUntil = new Date();
|
||||
validUntil.setTime(validUntil.getTime() + 30000);
|
||||
await expect(
|
||||
service.addToken(user, identifier, validUntil),
|
||||
).rejects.toThrow(TooManyTokensError);
|
||||
|
@ -400,17 +397,17 @@ describe('ApiTokenService', () => {
|
|||
expect(tokenDto.keyId).toEqual(apiToken.keyId);
|
||||
expect(tokenDto.lastUsedAt).toBeNull();
|
||||
expect(tokenDto.label).toEqual(apiToken.label);
|
||||
expect(tokenDto.validUntil.getTime()).toEqual(
|
||||
expect(new Date(tokenDto.validUntil).getTime()).toEqual(
|
||||
apiToken.validUntil.getTime(),
|
||||
);
|
||||
expect(tokenDto.createdAt.getTime()).toEqual(
|
||||
expect(new Date(tokenDto.createdAt).getTime()).toEqual(
|
||||
apiToken.createdAt.getTime(),
|
||||
);
|
||||
});
|
||||
it('should have lastUsedAt', () => {
|
||||
apiToken.lastUsedAt = new Date();
|
||||
const tokenDto = service.toAuthTokenDto(apiToken);
|
||||
expect(tokenDto.lastUsedAt).toEqual(apiToken.lastUsedAt);
|
||||
expect(tokenDto.lastUsedAt).toEqual(apiToken.lastUsedAt.toISOString());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ApiTokenDto, ApiTokenWithSecretDto } from '@hedgedoc/commons';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Cron, Timeout } from '@nestjs/schedule';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
@ -17,8 +18,6 @@ import {
|
|||
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
||||
import { User } from '../users/user.entity';
|
||||
import { bufferToBase64Url } from '../utils/password';
|
||||
import { TimestampMillis } from '../utils/timestamp';
|
||||
import { ApiTokenDto, ApiTokenWithSecretDto } from './api-token.dto';
|
||||
import { ApiToken } from './api-token.entity';
|
||||
|
||||
export const AUTH_TOKEN_PREFIX = 'hd2';
|
||||
|
@ -54,19 +53,19 @@ export class ApiTokenService {
|
|||
createToken(
|
||||
user: User,
|
||||
identifier: string,
|
||||
userDefinedValidUntil: TimestampMillis | undefined,
|
||||
userDefinedValidUntil: Date | null,
|
||||
): [Omit<ApiToken, 'id' | 'createdAt'>, string] {
|
||||
const secret = bufferToBase64Url(randomBytes(64));
|
||||
const keyId = bufferToBase64Url(randomBytes(8));
|
||||
// More about the choice of SHA-512 in the dev docs
|
||||
const accessTokenHash = createHash('sha512').update(secret).digest('hex');
|
||||
// Tokens can only be valid for a maximum of 2 years
|
||||
const maximumTokenValidity =
|
||||
new Date().getTime() + 2 * 365 * 24 * 60 * 60 * 1000;
|
||||
const maximumTokenValidity = new Date();
|
||||
maximumTokenValidity.setTime(
|
||||
maximumTokenValidity.getTime() + 2 * 365 * 24 * 60 * 60 * 1000,
|
||||
);
|
||||
const isTokenLimitedToMaximumValidity =
|
||||
!userDefinedValidUntil ||
|
||||
userDefinedValidUntil === 0 ||
|
||||
userDefinedValidUntil > maximumTokenValidity;
|
||||
!userDefinedValidUntil || userDefinedValidUntil > maximumTokenValidity;
|
||||
const validUntil = isTokenLimitedToMaximumValidity
|
||||
? maximumTokenValidity
|
||||
: userDefinedValidUntil;
|
||||
|
@ -83,7 +82,7 @@ export class ApiTokenService {
|
|||
async addToken(
|
||||
user: User,
|
||||
identifier: string,
|
||||
validUntil: TimestampMillis | undefined,
|
||||
validUntil: Date | null,
|
||||
): Promise<ApiTokenWithSecretDto> {
|
||||
user.apiTokens = this.getTokensByUser(user);
|
||||
|
||||
|
@ -176,13 +175,13 @@ export class ApiTokenService {
|
|||
const tokenDto: ApiTokenDto = {
|
||||
label: authToken.label,
|
||||
keyId: authToken.keyId,
|
||||
createdAt: authToken.createdAt,
|
||||
validUntil: authToken.validUntil,
|
||||
createdAt: authToken.createdAt.toISOString(),
|
||||
validUntil: authToken.validUntil.toISOString(),
|
||||
lastUsedAt: null,
|
||||
};
|
||||
|
||||
if (authToken.lastUsedAt) {
|
||||
tokenDto.lastUsedAt = new Date(authToken.lastUsedAt);
|
||||
tokenDto.lastUsedAt = new Date(authToken.lastUsedAt).toISOString();
|
||||
}
|
||||
|
||||
return tokenDto;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -23,7 +23,12 @@ export class MockApiTokenGuard {
|
|||
try {
|
||||
this.user = await this.usersService.getUserByUsername('hardcoded');
|
||||
} catch (e) {
|
||||
this.user = await this.usersService.createUser('hardcoded', 'Testy');
|
||||
this.user = await this.usersService.createUser(
|
||||
'hardcoded',
|
||||
'Testy',
|
||||
null,
|
||||
null,
|
||||
);
|
||||
}
|
||||
}
|
||||
req.user = this.user;
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { AliasCreateDto } from '@hedgedoc/commons';
|
||||
import { AliasUpdateDto } from '@hedgedoc/commons';
|
||||
import { AliasDto } from '@hedgedoc/commons';
|
||||
import {
|
||||
BadRequestException,
|
||||
Body,
|
||||
|
@ -18,9 +21,6 @@ import { ApiTags } from '@nestjs/swagger';
|
|||
|
||||
import { SessionGuard } from '../../../auth/session.guard';
|
||||
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||
import { AliasCreateDto } from '../../../notes/alias-create.dto';
|
||||
import { AliasUpdateDto } from '../../../notes/alias-update.dto';
|
||||
import { AliasDto } from '../../../notes/alias.dto';
|
||||
import { AliasService } from '../../../notes/alias.service';
|
||||
import { NotesService } from '../../../notes/notes.service';
|
||||
import { PermissionsService } from '../../../permissions/permissions.service';
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import {
|
||||
FullUserInfoDto,
|
||||
LogoutResponseDto,
|
||||
PendingUserConfirmationDto,
|
||||
ProviderType,
|
||||
} from '@hedgedoc/commons';
|
||||
import {
|
||||
BadRequestException,
|
||||
Body,
|
||||
|
@ -17,11 +23,8 @@ import { ApiTags } from '@nestjs/swagger';
|
|||
|
||||
import { IdentityService } from '../../../auth/identity.service';
|
||||
import { OidcService } from '../../../auth/oidc/oidc.service';
|
||||
import { PendingUserConfirmationDto } from '../../../auth/pending-user-confirmation.dto';
|
||||
import { ProviderType } from '../../../auth/provider-type.enum';
|
||||
import { RequestWithSession, SessionGuard } from '../../../auth/session.guard';
|
||||
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||
import { FullUserInfoDto } from '../../../users/user-info.dto';
|
||||
import { OpenApi } from '../../utils/openapi.decorator';
|
||||
|
||||
@ApiTags('auth')
|
||||
|
@ -38,7 +41,7 @@ export class AuthController {
|
|||
@UseGuards(SessionGuard)
|
||||
@Delete('logout')
|
||||
@OpenApi(200, 400, 401)
|
||||
logout(@Req() request: RequestWithSession): { redirect: string } {
|
||||
logout(@Req() request: RequestWithSession): LogoutResponseDto {
|
||||
let logoutUrl: string | null = null;
|
||||
if (request.session.authProviderType === ProviderType.OIDC) {
|
||||
logoutUrl = this.oidcService.getLogoutUrl(request);
|
||||
|
@ -60,9 +63,7 @@ export class AuthController {
|
|||
|
||||
@Get('pending-user')
|
||||
@OpenApi(200, 400)
|
||||
getPendingUserData(
|
||||
@Req() request: RequestWithSession,
|
||||
): Partial<FullUserInfoDto> {
|
||||
getPendingUserData(@Req() request: RequestWithSession): FullUserInfoDto {
|
||||
if (
|
||||
!request.session.newUserData ||
|
||||
!request.session.authProviderIdentifier ||
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import {
|
||||
LdapLoginDto,
|
||||
LdapLoginResponseDto,
|
||||
ProviderType,
|
||||
} from '@hedgedoc/commons';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
|
@ -14,14 +19,11 @@ import {
|
|||
import { ApiTags } from '@nestjs/swagger';
|
||||
|
||||
import { IdentityService } from '../../../../auth/identity.service';
|
||||
import { LdapLoginDto } from '../../../../auth/ldap/ldap-login.dto';
|
||||
import { LdapService } from '../../../../auth/ldap/ldap.service';
|
||||
import { ProviderType } from '../../../../auth/provider-type.enum';
|
||||
import { RequestWithSession } from '../../../../auth/session.guard';
|
||||
import { NotInDBError } from '../../../../errors/errors';
|
||||
import { ConsoleLoggerService } from '../../../../logger/console-logger.service';
|
||||
import { UsersService } from '../../../../users/users.service';
|
||||
import { makeUsernameLowercase } from '../../../../utils/username';
|
||||
import { OpenApi } from '../../../utils/openapi.decorator';
|
||||
|
||||
@ApiTags('auth')
|
||||
|
@ -43,7 +45,7 @@ export class LdapController {
|
|||
request: RequestWithSession,
|
||||
@Param('ldapIdentifier') ldapIdentifier: string,
|
||||
@Body() loginDto: LdapLoginDto,
|
||||
): Promise<{ newUser: boolean }> {
|
||||
): Promise<LdapLoginResponseDto> {
|
||||
const ldapConfig = this.ldapService.getLdapConfig(ldapIdentifier);
|
||||
const userInfo = await this.ldapService.getUserInfoFromLdap(
|
||||
ldapConfig,
|
||||
|
@ -61,7 +63,7 @@ export class LdapController {
|
|||
);
|
||||
if (this.identityService.mayUpdateIdentity(ldapIdentifier)) {
|
||||
const user = await this.usersService.getUserByUsername(
|
||||
makeUsernameLowercase(loginDto.username),
|
||||
loginDto.username.toLowerCase(),
|
||||
);
|
||||
await this.usersService.updateUser(
|
||||
user,
|
||||
|
@ -70,7 +72,7 @@ export class LdapController {
|
|||
userInfo.photoUrl,
|
||||
);
|
||||
}
|
||||
request.session.username = makeUsernameLowercase(loginDto.username);
|
||||
request.session.username = loginDto.username;
|
||||
return { newUser: false };
|
||||
} catch (error) {
|
||||
if (error instanceof NotInDBError) {
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import {
|
||||
LoginDto,
|
||||
ProviderType,
|
||||
RegisterDto,
|
||||
UpdatePasswordDto,
|
||||
} from '@hedgedoc/commons';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
|
@ -15,10 +21,6 @@ import {
|
|||
import { ApiTags } from '@nestjs/swagger';
|
||||
|
||||
import { LocalService } from '../../../../auth/local/local.service';
|
||||
import { LoginDto } from '../../../../auth/local/login.dto';
|
||||
import { RegisterDto } from '../../../../auth/local/register.dto';
|
||||
import { UpdatePasswordDto } from '../../../../auth/local/update-password.dto';
|
||||
import { ProviderType } from '../../../../auth/provider-type.enum';
|
||||
import {
|
||||
RequestWithSession,
|
||||
SessionGuard,
|
||||
|
@ -53,6 +55,8 @@ export class LocalController {
|
|||
const user = await this.usersService.createUser(
|
||||
registerDto.username,
|
||||
registerDto.displayName,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
await this.localIdentityService.createLocalIdentity(
|
||||
user,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ProviderType } from '@hedgedoc/commons';
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
|
@ -17,7 +18,6 @@ import { ApiTags } from '@nestjs/swagger';
|
|||
|
||||
import { IdentityService } from '../../../../auth/identity.service';
|
||||
import { OidcService } from '../../../../auth/oidc/oidc.service';
|
||||
import { ProviderType } from '../../../../auth/provider-type.enum';
|
||||
import { RequestWithSession } from '../../../../auth/session.guard';
|
||||
import { ConsoleLoggerService } from '../../../../logger/console-logger.service';
|
||||
import { UsersService } from '../../../../users/users.service';
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { FrontendConfigDto } from '@hedgedoc/commons';
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
|
||||
import { FrontendConfigDto } from '../../../frontend-config/frontend-config.dto';
|
||||
import { FrontendConfigService } from '../../../frontend-config/frontend-config.service';
|
||||
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||
import { OpenApi } from '../../utils/openapi.decorator';
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { GroupInfoDto } from '@hedgedoc/commons';
|
||||
import { Controller, Get, Param, UseGuards } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
|
||||
import { SessionGuard } from '../../../auth/session.guard';
|
||||
import { GroupInfoDto } from '../../../groups/group-info.dto';
|
||||
import { GroupsService } from '../../../groups/groups.service';
|
||||
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||
import { OpenApi } from '../../utils/openapi.decorator';
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { Body, Controller, Delete, Get, Post, UseGuards } from '@nestjs/common';
|
||||
import { ApiBody, ApiTags } from '@nestjs/swagger';
|
||||
import {
|
||||
LoginUserInfoDto,
|
||||
MediaUploadDto,
|
||||
ProviderType,
|
||||
} from '@hedgedoc/commons';
|
||||
import { Body, Controller, Delete, Get, Put, UseGuards } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
|
||||
import { SessionGuard } from '../../../auth/session.guard';
|
||||
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||
import { MediaUploadDto } from '../../../media/media-upload.dto';
|
||||
import { MediaService } from '../../../media/media.service';
|
||||
import { UserLoginInfoDto } from '../../../users/user-info.dto';
|
||||
import { User } from '../../../users/user.entity';
|
||||
import { UsersService } from '../../../users/users.service';
|
||||
import { OpenApi } from '../../utils/openapi.decorator';
|
||||
|
@ -34,9 +37,9 @@ export class MeController {
|
|||
@OpenApi(200)
|
||||
getMe(
|
||||
@RequestUser() user: User,
|
||||
@SessionAuthProvider() authProvider: string,
|
||||
): UserLoginInfoDto {
|
||||
return this.userService.toUserLoginInfoDto(user, authProvider);
|
||||
@SessionAuthProvider() authProvider: ProviderType,
|
||||
): LoginUserInfoDto {
|
||||
return this.userService.toLoginUserInfoDto(user, authProvider);
|
||||
}
|
||||
|
||||
@Get('media')
|
||||
|
@ -60,18 +63,9 @@ export class MeController {
|
|||
this.logger.debug(`Deleted ${user.username}`);
|
||||
}
|
||||
|
||||
@Post('profile')
|
||||
@ApiBody({
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
displayName: { type: 'string', nullable: false },
|
||||
},
|
||||
required: ['displayName'],
|
||||
},
|
||||
})
|
||||
@Put('profile')
|
||||
@OpenApi(200)
|
||||
async updateDisplayName(
|
||||
async updateProfile(
|
||||
@RequestUser() user: User,
|
||||
@Body('displayName') newDisplayName: string,
|
||||
): Promise<void> {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { MediaUploadDto, MediaUploadSchema } from '@hedgedoc/commons';
|
||||
import {
|
||||
BadRequestException,
|
||||
Controller,
|
||||
|
@ -22,7 +23,6 @@ import { Response } from 'express';
|
|||
import { SessionGuard } from '../../../auth/session.guard';
|
||||
import { PermissionError } from '../../../errors/errors';
|
||||
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||
import { MediaUploadDto } from '../../../media/media-upload.dto';
|
||||
import { MediaService } from '../../../media/media.service';
|
||||
import { MulterFile } from '../../../media/multer-file.interface';
|
||||
import { Note } from '../../../notes/note.entity';
|
||||
|
@ -74,7 +74,7 @@ export class MediaController {
|
|||
{
|
||||
code: 201,
|
||||
description: 'The file was uploaded successfully',
|
||||
dto: MediaUploadDto,
|
||||
schema: MediaUploadSchema,
|
||||
},
|
||||
400,
|
||||
403,
|
||||
|
|
|
@ -1,8 +1,22 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import {
|
||||
ChangeNoteOwnerDto,
|
||||
MediaUploadDto,
|
||||
NoteDto,
|
||||
NoteGroupPermissionEntryDto,
|
||||
NoteGroupPermissionUpdateDto,
|
||||
NoteMediaDeletionDto,
|
||||
NoteMetadataDto,
|
||||
NotePermissionsDto,
|
||||
NoteUserPermissionEntryDto,
|
||||
NoteUserPermissionUpdateDto,
|
||||
RevisionDto,
|
||||
RevisionMetadataDto,
|
||||
} from '@hedgedoc/commons';
|
||||
import {
|
||||
BadRequestException,
|
||||
Body,
|
||||
|
@ -22,24 +36,16 @@ import { NotInDBError } from '../../../errors/errors';
|
|||
import { GroupsService } from '../../../groups/groups.service';
|
||||
import { HistoryService } from '../../../history/history.service';
|
||||
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||
import { MediaUploadDto } from '../../../media/media-upload.dto';
|
||||
import { MediaService } from '../../../media/media.service';
|
||||
import { NoteMetadataDto } from '../../../notes/note-metadata.dto';
|
||||
import { NotePermissionsDto } from '../../../notes/note-permissions.dto';
|
||||
import { NoteDto } from '../../../notes/note.dto';
|
||||
import { Note } from '../../../notes/note.entity';
|
||||
import { NoteMediaDeletionDto } from '../../../notes/note.media-deletion.dto';
|
||||
import { NotesService } from '../../../notes/notes.service';
|
||||
import { PermissionsGuard } from '../../../permissions/permissions.guard';
|
||||
import { PermissionsService } from '../../../permissions/permissions.service';
|
||||
import { RequirePermission } from '../../../permissions/require-permission.decorator';
|
||||
import { RequiredPermission } from '../../../permissions/required-permission.enum';
|
||||
import { RevisionMetadataDto } from '../../../revisions/revision-metadata.dto';
|
||||
import { RevisionDto } from '../../../revisions/revision.dto';
|
||||
import { RevisionsService } from '../../../revisions/revisions.service';
|
||||
import { User } from '../../../users/user.entity';
|
||||
import { UsersService } from '../../../users/users.service';
|
||||
import { Username } from '../../../utils/username';
|
||||
import { GetNoteInterceptor } from '../../utils/get-note.interceptor';
|
||||
import { MarkdownBody } from '../../utils/markdown-body.decorator';
|
||||
import { OpenApi } from '../../utils/openapi.decorator';
|
||||
|
@ -197,15 +203,15 @@ export class NotesController {
|
|||
);
|
||||
}
|
||||
|
||||
@Put(':noteIdOrAlias/metadata/permissions/users/:userName')
|
||||
@Put(':noteIdOrAlias/metadata/permissions/users/:username')
|
||||
@OpenApi(200, 403, 404)
|
||||
@UseInterceptors(GetNoteInterceptor)
|
||||
@RequirePermission(RequiredPermission.OWNER)
|
||||
async setUserPermission(
|
||||
@RequestUser() user: User,
|
||||
@RequestNote() note: Note,
|
||||
@Param('userName') username: Username,
|
||||
@Body('canEdit') canEdit: boolean,
|
||||
@Param('username') username: NoteUserPermissionUpdateDto['username'],
|
||||
@Body('canEdit') canEdit: NoteUserPermissionUpdateDto['canEdit'],
|
||||
): Promise<NotePermissionsDto> {
|
||||
const permissionUser = await this.userService.getUserByUsername(username);
|
||||
const returnedNote = await this.permissionService.setUserPermission(
|
||||
|
@ -218,11 +224,11 @@ export class NotesController {
|
|||
|
||||
@UseInterceptors(GetNoteInterceptor)
|
||||
@RequirePermission(RequiredPermission.OWNER)
|
||||
@Delete(':noteIdOrAlias/metadata/permissions/users/:userName')
|
||||
@Delete(':noteIdOrAlias/metadata/permissions/users/:username')
|
||||
async removeUserPermission(
|
||||
@RequestUser() user: User,
|
||||
@RequestNote() note: Note,
|
||||
@Param('userName') username: Username,
|
||||
@Param('username') username: NoteUserPermissionEntryDto['username'],
|
||||
): Promise<NotePermissionsDto> {
|
||||
try {
|
||||
const permissionUser = await this.userService.getUserByUsername(username);
|
||||
|
@ -247,8 +253,8 @@ export class NotesController {
|
|||
async setGroupPermission(
|
||||
@RequestUser() user: User,
|
||||
@RequestNote() note: Note,
|
||||
@Param('groupName') groupName: string,
|
||||
@Body('canEdit') canEdit: boolean,
|
||||
@Param('groupName') groupName: NoteGroupPermissionUpdateDto['groupName'],
|
||||
@Body('canEdit') canEdit: NoteGroupPermissionUpdateDto['canEdit'],
|
||||
): Promise<NotePermissionsDto> {
|
||||
const permissionGroup = await this.groupService.getGroupByName(groupName);
|
||||
const returnedNote = await this.permissionService.setGroupPermission(
|
||||
|
@ -266,7 +272,7 @@ export class NotesController {
|
|||
async removeGroupPermission(
|
||||
@RequestUser() user: User,
|
||||
@RequestNote() note: Note,
|
||||
@Param('groupName') groupName: string,
|
||||
@Param('groupName') groupName: NoteGroupPermissionEntryDto['groupName'],
|
||||
): Promise<NotePermissionsDto> {
|
||||
const permissionGroup = await this.groupService.getGroupByName(groupName);
|
||||
const returnedNote = await this.permissionService.removeGroupPermission(
|
||||
|
@ -282,9 +288,11 @@ export class NotesController {
|
|||
async changeOwner(
|
||||
@RequestUser() user: User,
|
||||
@RequestNote() note: Note,
|
||||
@Body('newOwner') newOwner: Username,
|
||||
@Body() changeNoteOwnerDto: ChangeNoteOwnerDto,
|
||||
): Promise<NoteDto> {
|
||||
const owner = await this.userService.getUserByUsername(newOwner);
|
||||
const owner = await this.userService.getUserByUsername(
|
||||
changeNoteOwnerDto.owner,
|
||||
);
|
||||
return await this.noteService.toNoteDto(
|
||||
await this.permissionService.changeOwner(note, owner),
|
||||
);
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import {
|
||||
ApiTokenCreateDto,
|
||||
ApiTokenDto,
|
||||
ApiTokenWithSecretDto,
|
||||
} from '@hedgedoc/commons';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
|
@ -15,11 +20,6 @@ import {
|
|||
} from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
|
||||
import {
|
||||
ApiTokenCreateDto,
|
||||
ApiTokenDto,
|
||||
ApiTokenWithSecretDto,
|
||||
} from '../../../api-token/api-token.dto';
|
||||
import { ApiTokenService } from '../../../api-token/api-token.service';
|
||||
import { SessionGuard } from '../../../auth/session.guard';
|
||||
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import {
|
||||
UserInfoDto,
|
||||
UsernameCheckDto,
|
||||
UsernameCheckResponseDto,
|
||||
} from '@hedgedoc/commons';
|
||||
import { Body, Controller, Get, HttpCode, Param, Post } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
|
||||
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||
import { UserInfoDto } from '../../../users/user-info.dto';
|
||||
import {
|
||||
UsernameCheckDto,
|
||||
UsernameCheckResponseDto,
|
||||
} from '../../../users/username-check.dto';
|
||||
import { UsersService } from '../../../users/users.service';
|
||||
import { Username } from '../../../utils/username';
|
||||
import { OpenApi } from '../../utils/openapi.decorator';
|
||||
|
||||
@ApiTags('users')
|
||||
|
@ -41,7 +40,7 @@ export class UsersController {
|
|||
|
||||
@Get('profile/:username')
|
||||
@OpenApi(200)
|
||||
async getUser(@Param('username') username: Username): Promise<UserInfoDto> {
|
||||
async getUser(@Param('username') username: string): Promise<UserInfoDto> {
|
||||
return this.userService.toUserDto(
|
||||
await this.userService.getUserByUsername(username),
|
||||
);
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import {
|
||||
AliasCreateDto,
|
||||
AliasDto,
|
||||
AliasSchema,
|
||||
AliasUpdateDto,
|
||||
} from '@hedgedoc/commons';
|
||||
import {
|
||||
BadRequestException,
|
||||
Body,
|
||||
|
@ -18,9 +24,6 @@ import { ApiSecurity, ApiTags } from '@nestjs/swagger';
|
|||
|
||||
import { ApiTokenGuard } from '../../../api-token/api-token.guard';
|
||||
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||
import { AliasCreateDto } from '../../../notes/alias-create.dto';
|
||||
import { AliasUpdateDto } from '../../../notes/alias-update.dto';
|
||||
import { AliasDto } from '../../../notes/alias.dto';
|
||||
import { AliasService } from '../../../notes/alias.service';
|
||||
import { NotesService } from '../../../notes/notes.service';
|
||||
import { PermissionsService } from '../../../permissions/permissions.service';
|
||||
|
@ -48,7 +51,7 @@ export class AliasController {
|
|||
{
|
||||
code: 200,
|
||||
description: 'The new alias',
|
||||
dto: AliasDto,
|
||||
schema: AliasSchema,
|
||||
},
|
||||
403,
|
||||
404,
|
||||
|
@ -75,7 +78,7 @@ export class AliasController {
|
|||
{
|
||||
code: 200,
|
||||
description: 'The updated alias',
|
||||
dto: AliasDto,
|
||||
schema: AliasSchema,
|
||||
},
|
||||
403,
|
||||
404,
|
||||
|
@ -110,7 +113,7 @@ export class AliasController {
|
|||
)
|
||||
async removeAlias(
|
||||
@RequestUser() user: User,
|
||||
@Param('alias') alias: string,
|
||||
@Param('alias') alias: AliasDto['name'],
|
||||
): Promise<void> {
|
||||
const note = await this.noteService.getNoteByIdOrAlias(alias);
|
||||
if (!(await this.permissionsService.isOwner(user, note))) {
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import {
|
||||
FullUserInfoDto,
|
||||
FullUserInfoSchema,
|
||||
MediaUploadDto,
|
||||
MediaUploadSchema,
|
||||
NoteMetadataDto,
|
||||
NoteMetadataSchema,
|
||||
} from '@hedgedoc/commons';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
|
@ -19,12 +27,9 @@ import { HistoryEntryUpdateDto } from '../../../history/history-entry-update.dto
|
|||
import { HistoryEntryDto } from '../../../history/history-entry.dto';
|
||||
import { HistoryService } from '../../../history/history.service';
|
||||
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||
import { MediaUploadDto } from '../../../media/media-upload.dto';
|
||||
import { MediaService } from '../../../media/media.service';
|
||||
import { NoteMetadataDto } from '../../../notes/note-metadata.dto';
|
||||
import { Note } from '../../../notes/note.entity';
|
||||
import { NotesService } from '../../../notes/notes.service';
|
||||
import { FullUserInfoDto } from '../../../users/user-info.dto';
|
||||
import { User } from '../../../users/user.entity';
|
||||
import { UsersService } from '../../../users/users.service';
|
||||
import { GetNoteInterceptor } from '../../utils/get-note.interceptor';
|
||||
|
@ -52,7 +57,7 @@ export class MeController {
|
|||
@OpenApi({
|
||||
code: 200,
|
||||
description: 'The user information',
|
||||
dto: FullUserInfoDto,
|
||||
schema: FullUserInfoSchema,
|
||||
})
|
||||
getMe(@RequestUser() user: User): FullUserInfoDto {
|
||||
return this.usersService.toFullUserDto(user);
|
||||
|
@ -63,7 +68,6 @@ export class MeController {
|
|||
code: 200,
|
||||
description: 'The history entries of the user',
|
||||
isArray: true,
|
||||
dto: HistoryEntryDto,
|
||||
})
|
||||
async getUserHistory(@RequestUser() user: User): Promise<HistoryEntryDto[]> {
|
||||
const foundEntries = await this.historyService.getEntriesByUser(user);
|
||||
|
@ -78,7 +82,6 @@ export class MeController {
|
|||
{
|
||||
code: 200,
|
||||
description: 'The history entry of the user which points to the note',
|
||||
dto: HistoryEntryDto,
|
||||
},
|
||||
404,
|
||||
)
|
||||
|
@ -96,7 +99,6 @@ export class MeController {
|
|||
{
|
||||
code: 200,
|
||||
description: 'The updated history entry',
|
||||
dto: HistoryEntryDto,
|
||||
},
|
||||
404,
|
||||
)
|
||||
|
@ -125,7 +127,7 @@ export class MeController {
|
|||
code: 200,
|
||||
description: 'Metadata of all notes of the user',
|
||||
isArray: true,
|
||||
dto: NoteMetadataDto,
|
||||
schema: NoteMetadataSchema,
|
||||
})
|
||||
async getMyNotes(@RequestUser() user: User): Promise<NoteMetadataDto[]> {
|
||||
const notes = this.notesService.getUserNotes(user);
|
||||
|
@ -139,7 +141,7 @@ export class MeController {
|
|||
code: 200,
|
||||
description: 'All media uploads of the user',
|
||||
isArray: true,
|
||||
dto: MediaUploadDto,
|
||||
schema: MediaUploadSchema,
|
||||
})
|
||||
async getMyMedia(@RequestUser() user: User): Promise<MediaUploadDto[]> {
|
||||
const media = await this.mediaService.listUploadsByUser(user);
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { MediaUploadDto, MediaUploadSchema } from '@hedgedoc/commons';
|
||||
import {
|
||||
BadRequestException,
|
||||
Controller,
|
||||
|
@ -28,7 +29,6 @@ import { Response } from 'express';
|
|||
import { ApiTokenGuard } from '../../../api-token/api-token.guard';
|
||||
import { PermissionError } from '../../../errors/errors';
|
||||
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||
import { MediaUploadDto } from '../../../media/media-upload.dto';
|
||||
import { MediaService } from '../../../media/media.service';
|
||||
import { MulterFile } from '../../../media/multer-file.interface';
|
||||
import { Note } from '../../../notes/note.entity';
|
||||
|
@ -77,7 +77,7 @@ export class MediaController {
|
|||
{
|
||||
code: 201,
|
||||
description: 'The file was uploaded successfully',
|
||||
dto: MediaUploadDto,
|
||||
schema: MediaUploadSchema,
|
||||
},
|
||||
400,
|
||||
403,
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ServerStatusDto, ServerStatusSchema } from '@hedgedoc/commons';
|
||||
import { Controller, Get, UseGuards } from '@nestjs/common';
|
||||
import { ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
|
||||
import { ApiTokenGuard } from '../../../api-token/api-token.guard';
|
||||
import { MonitoringService } from '../../../monitoring/monitoring.service';
|
||||
import { ServerStatusDto } from '../../../monitoring/server-status.dto';
|
||||
import { OpenApi } from '../../utils/openapi.decorator';
|
||||
|
||||
@UseGuards(ApiTokenGuard)
|
||||
|
@ -24,7 +24,7 @@ export class MonitoringController {
|
|||
{
|
||||
code: 200,
|
||||
description: 'The server info',
|
||||
dto: ServerStatusDto,
|
||||
schema: ServerStatusSchema,
|
||||
},
|
||||
403,
|
||||
)
|
||||
|
|
|
@ -1,8 +1,24 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import {
|
||||
MediaUploadDto,
|
||||
MediaUploadSchema,
|
||||
NoteDto,
|
||||
NoteMediaDeletionDto,
|
||||
NoteMetadataDto,
|
||||
NoteMetadataSchema,
|
||||
NotePermissionsDto,
|
||||
NotePermissionsSchema,
|
||||
NotePermissionsUpdateDto,
|
||||
NoteSchema,
|
||||
RevisionDto,
|
||||
RevisionMetadataDto,
|
||||
RevisionMetadataSchema,
|
||||
RevisionSchema,
|
||||
} from '@hedgedoc/commons';
|
||||
import {
|
||||
BadRequestException,
|
||||
Body,
|
||||
|
@ -22,27 +38,16 @@ import { NotInDBError } from '../../../errors/errors';
|
|||
import { GroupsService } from '../../../groups/groups.service';
|
||||
import { HistoryService } from '../../../history/history.service';
|
||||
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||
import { MediaUploadDto } from '../../../media/media-upload.dto';
|
||||
import { MediaService } from '../../../media/media.service';
|
||||
import { NoteMetadataDto } from '../../../notes/note-metadata.dto';
|
||||
import {
|
||||
NotePermissionsDto,
|
||||
NotePermissionsUpdateDto,
|
||||
} from '../../../notes/note-permissions.dto';
|
||||
import { NoteDto } from '../../../notes/note.dto';
|
||||
import { Note } from '../../../notes/note.entity';
|
||||
import { NoteMediaDeletionDto } from '../../../notes/note.media-deletion.dto';
|
||||
import { NotesService } from '../../../notes/notes.service';
|
||||
import { PermissionsGuard } from '../../../permissions/permissions.guard';
|
||||
import { PermissionsService } from '../../../permissions/permissions.service';
|
||||
import { RequirePermission } from '../../../permissions/require-permission.decorator';
|
||||
import { RequiredPermission } from '../../../permissions/required-permission.enum';
|
||||
import { RevisionMetadataDto } from '../../../revisions/revision-metadata.dto';
|
||||
import { RevisionDto } from '../../../revisions/revision.dto';
|
||||
import { RevisionsService } from '../../../revisions/revisions.service';
|
||||
import { User } from '../../../users/user.entity';
|
||||
import { UsersService } from '../../../users/users.service';
|
||||
import { Username } from '../../../utils/username';
|
||||
import { GetNoteInterceptor } from '../../utils/get-note.interceptor';
|
||||
import { MarkdownBody } from '../../utils/markdown-body.decorator';
|
||||
import { OpenApi } from '../../utils/openapi.decorator';
|
||||
|
@ -88,7 +93,7 @@ export class NotesController {
|
|||
{
|
||||
code: 200,
|
||||
description: 'Get information about the newly created note',
|
||||
dto: NoteDto,
|
||||
schema: NoteSchema,
|
||||
},
|
||||
403,
|
||||
404,
|
||||
|
@ -107,7 +112,7 @@ export class NotesController {
|
|||
{
|
||||
code: 201,
|
||||
description: 'Get information about the newly created note',
|
||||
dto: NoteDto,
|
||||
schema: NoteSchema,
|
||||
},
|
||||
400,
|
||||
403,
|
||||
|
@ -155,7 +160,7 @@ export class NotesController {
|
|||
{
|
||||
code: 200,
|
||||
description: 'The new, changed note',
|
||||
dto: NoteDto,
|
||||
schema: NoteSchema,
|
||||
},
|
||||
403,
|
||||
404,
|
||||
|
@ -197,7 +202,7 @@ export class NotesController {
|
|||
{
|
||||
code: 200,
|
||||
description: 'The metadata of the note',
|
||||
dto: NoteMetadataDto,
|
||||
schema: NoteMetadataSchema,
|
||||
},
|
||||
403,
|
||||
404,
|
||||
|
@ -216,7 +221,7 @@ export class NotesController {
|
|||
{
|
||||
code: 200,
|
||||
description: 'The updated permissions of the note',
|
||||
dto: NotePermissionsDto,
|
||||
schema: NotePermissionsSchema,
|
||||
},
|
||||
403,
|
||||
404,
|
||||
|
@ -238,7 +243,7 @@ export class NotesController {
|
|||
{
|
||||
code: 200,
|
||||
description: 'Get the permissions for a note',
|
||||
dto: NotePermissionsDto,
|
||||
schema: NotePermissionsSchema,
|
||||
},
|
||||
403,
|
||||
404,
|
||||
|
@ -257,7 +262,7 @@ export class NotesController {
|
|||
{
|
||||
code: 200,
|
||||
description: 'Set the permissions for a user on a note',
|
||||
dto: NotePermissionsDto,
|
||||
schema: NotePermissionsSchema,
|
||||
},
|
||||
403,
|
||||
404,
|
||||
|
@ -265,7 +270,7 @@ export class NotesController {
|
|||
async setUserPermission(
|
||||
@RequestUser() user: User,
|
||||
@RequestNote() note: Note,
|
||||
@Param('userName') username: Username,
|
||||
@Param('userName') username: string,
|
||||
@Body('canEdit') canEdit: boolean,
|
||||
): Promise<NotePermissionsDto> {
|
||||
const permissionUser = await this.userService.getUserByUsername(username);
|
||||
|
@ -284,7 +289,7 @@ export class NotesController {
|
|||
{
|
||||
code: 200,
|
||||
description: 'Remove the permission for a user on a note',
|
||||
dto: NotePermissionsDto,
|
||||
schema: NotePermissionsSchema,
|
||||
},
|
||||
403,
|
||||
404,
|
||||
|
@ -292,7 +297,7 @@ export class NotesController {
|
|||
async removeUserPermission(
|
||||
@RequestUser() user: User,
|
||||
@RequestNote() note: Note,
|
||||
@Param('userName') username: Username,
|
||||
@Param('userName') username: string,
|
||||
): Promise<NotePermissionsDto> {
|
||||
try {
|
||||
const permissionUser = await this.userService.getUserByUsername(username);
|
||||
|
@ -318,7 +323,7 @@ export class NotesController {
|
|||
{
|
||||
code: 200,
|
||||
description: 'Set the permissions for a group on a note',
|
||||
dto: NotePermissionsDto,
|
||||
schema: NotePermissionsSchema,
|
||||
},
|
||||
403,
|
||||
404,
|
||||
|
@ -345,7 +350,7 @@ export class NotesController {
|
|||
{
|
||||
code: 200,
|
||||
description: 'Remove the permission for a group on a note',
|
||||
dto: NotePermissionsDto,
|
||||
schema: NotePermissionsSchema,
|
||||
},
|
||||
403,
|
||||
404,
|
||||
|
@ -370,7 +375,7 @@ export class NotesController {
|
|||
{
|
||||
code: 200,
|
||||
description: 'Changes the owner of the note',
|
||||
dto: NoteDto,
|
||||
schema: NoteSchema,
|
||||
},
|
||||
403,
|
||||
404,
|
||||
|
@ -378,7 +383,7 @@ export class NotesController {
|
|||
async changeOwner(
|
||||
@RequestUser() user: User,
|
||||
@RequestNote() note: Note,
|
||||
@Body('newOwner') newOwner: Username,
|
||||
@Body('newOwner') newOwner: string,
|
||||
): Promise<NoteDto> {
|
||||
const owner = await this.userService.getUserByUsername(newOwner);
|
||||
return await this.noteService.toNoteDto(
|
||||
|
@ -394,7 +399,7 @@ export class NotesController {
|
|||
code: 200,
|
||||
description: 'Revisions of the note',
|
||||
isArray: true,
|
||||
dto: RevisionMetadataDto,
|
||||
schema: RevisionMetadataSchema,
|
||||
},
|
||||
403,
|
||||
404,
|
||||
|
@ -418,7 +423,7 @@ export class NotesController {
|
|||
{
|
||||
code: 200,
|
||||
description: 'Revision of the note for the given id or alias',
|
||||
dto: RevisionDto,
|
||||
schema: RevisionSchema,
|
||||
},
|
||||
403,
|
||||
404,
|
||||
|
@ -440,7 +445,7 @@ export class NotesController {
|
|||
code: 200,
|
||||
description: 'All media uploads of the note',
|
||||
isArray: true,
|
||||
dto: MediaUploadDto,
|
||||
schema: MediaUploadSchema,
|
||||
})
|
||||
async getNotesMedia(
|
||||
@RequestUser() user: User,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -14,10 +14,12 @@ import {
|
|||
ApiNotFoundResponse,
|
||||
ApiOkResponse,
|
||||
ApiProduces,
|
||||
ApiResponseNoStatusOptions,
|
||||
ApiUnauthorizedResponse,
|
||||
} from '@nestjs/swagger';
|
||||
import { zodToOpenAPI } from 'nestjs-zod';
|
||||
import { ZodSchema } from 'zod';
|
||||
|
||||
import { BaseDto } from '../../utils/base.dto.';
|
||||
import {
|
||||
badRequestDescription,
|
||||
conflictDescription,
|
||||
|
@ -57,7 +59,7 @@ export interface HttpStatusCodeWithExtraInformation {
|
|||
code: HttpStatusCodes;
|
||||
description?: string;
|
||||
isArray?: boolean;
|
||||
dto?: BaseDto;
|
||||
schema?: ZodSchema;
|
||||
mimeType?: string;
|
||||
}
|
||||
|
||||
|
@ -89,7 +91,8 @@ export const OpenApi = (
|
|||
let code: HttpStatusCodes = 200;
|
||||
let description: string | undefined = undefined;
|
||||
let isArray: boolean | undefined = undefined;
|
||||
let dto: BaseDto | undefined = undefined;
|
||||
let schema: ZodSchema | undefined = undefined;
|
||||
|
||||
if (typeof entry == 'number') {
|
||||
code = entry;
|
||||
} else {
|
||||
|
@ -97,7 +100,7 @@ export const OpenApi = (
|
|||
code = entry.code;
|
||||
description = entry.description;
|
||||
isArray = entry.isArray;
|
||||
dto = entry.dto;
|
||||
schema = entry.schema;
|
||||
if (entry.mimeType) {
|
||||
decoratorsToApply.push(
|
||||
ApiProduces(entry.mimeType),
|
||||
|
@ -105,22 +108,32 @@ export const OpenApi = (
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
let defaultResponseObject: ApiResponseNoStatusOptions = {
|
||||
description: description ?? createdDescription,
|
||||
isArray: isArray,
|
||||
};
|
||||
if (schema) {
|
||||
defaultResponseObject = {
|
||||
...defaultResponseObject,
|
||||
schema: zodToOpenAPI(schema),
|
||||
};
|
||||
}
|
||||
|
||||
switch (code) {
|
||||
case 200:
|
||||
decoratorsToApply.push(
|
||||
ApiOkResponse({
|
||||
...defaultResponseObject,
|
||||
description: description ?? okDescription,
|
||||
isArray: isArray,
|
||||
type: dto ? (): BaseDto => dto : undefined,
|
||||
}),
|
||||
);
|
||||
break;
|
||||
case 201:
|
||||
decoratorsToApply.push(
|
||||
ApiCreatedResponse({
|
||||
...defaultResponseObject,
|
||||
description: description ?? createdDescription,
|
||||
isArray: isArray,
|
||||
type: dto ? (): BaseDto => dto : undefined,
|
||||
}),
|
||||
HttpCode(201),
|
||||
);
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ProviderType } from '@hedgedoc/commons';
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
|
@ -13,7 +14,6 @@ import {
|
|||
} from 'typeorm';
|
||||
|
||||
import { User } from '../users/user.entity';
|
||||
import { ProviderType } from './provider-type.enum';
|
||||
|
||||
/**
|
||||
* The identity represents a single way for a user to login.
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import {
|
||||
FullUserInfoDto,
|
||||
PendingUserConfirmationDto,
|
||||
ProviderType,
|
||||
} from '@hedgedoc/commons';
|
||||
import {
|
||||
Inject,
|
||||
Injectable,
|
||||
|
@ -14,12 +19,9 @@ import { DataSource, Repository } from 'typeorm';
|
|||
import AuthConfiguration, { AuthConfig } from '../config/auth.config';
|
||||
import { NotInDBError } from '../errors/errors';
|
||||
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
||||
import { FullUserInfoDto } from '../users/user-info.dto';
|
||||
import { User } from '../users/user.entity';
|
||||
import { UsersService } from '../users/users.service';
|
||||
import { Identity } from './identity.entity';
|
||||
import { PendingUserConfirmationDto } from './pending-user-confirmation.dto';
|
||||
import { ProviderType } from './provider-type.enum';
|
||||
|
||||
@Injectable()
|
||||
export class IdentityService {
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { IsString } from 'class-validator';
|
||||
|
||||
export class LdapLoginDto {
|
||||
@IsString()
|
||||
username: string; // This is not of type Username, because LDAP server may use mixed case usernames
|
||||
@IsString()
|
||||
password: string;
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { FullUserInfoWithIdDto } from '@hedgedoc/commons';
|
||||
import {
|
||||
Inject,
|
||||
Injectable,
|
||||
|
@ -18,8 +19,6 @@ import authConfiguration, {
|
|||
LDAPConfig,
|
||||
} from '../../config/auth.config';
|
||||
import { ConsoleLoggerService } from '../../logger/console-logger.service';
|
||||
import { FullUserInfoWithIdDto } from '../../users/user-info.dto';
|
||||
import { Username } from '../../utils/username';
|
||||
|
||||
const LDAP_ERROR_MAP: Record<string, string> = {
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
|
@ -96,7 +95,7 @@ export class LdapService {
|
|||
return reject(new UnauthorizedException(LDAP_ERROR_MAP['default']));
|
||||
}
|
||||
|
||||
let email: string | undefined = undefined;
|
||||
let email: string | null = null;
|
||||
if (userInfo['mail']) {
|
||||
if (Array.isArray(userInfo['mail'])) {
|
||||
email = userInfo['mail'][0] as string;
|
||||
|
@ -107,10 +106,10 @@ export class LdapService {
|
|||
|
||||
return resolve({
|
||||
email,
|
||||
username: username as Username,
|
||||
username: username,
|
||||
id: userInfo[ldapConfig.userIdField],
|
||||
displayName: userInfo[ldapConfig.displayNameField] ?? username,
|
||||
photoUrl: undefined, // TODO LDAP stores images as binaries,
|
||||
photoUrl: null, // TODO LDAP stores images as binaries,
|
||||
// we need to convert them into a data-URL or alike
|
||||
});
|
||||
},
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ProviderType } from '@hedgedoc/commons';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import {
|
||||
|
@ -32,7 +33,6 @@ import { User } from '../../users/user.entity';
|
|||
import { checkPassword, hashPassword } from '../../utils/password';
|
||||
import { Identity } from '../identity.entity';
|
||||
import { IdentityService } from '../identity.service';
|
||||
import { ProviderType } from '../provider-type.enum';
|
||||
|
||||
@Injectable()
|
||||
export class LocalService {
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsLowercase, IsString } from 'class-validator';
|
||||
|
||||
import { Username } from '../../utils/username';
|
||||
|
||||
export class LoginDto {
|
||||
@Type(() => String)
|
||||
@IsString()
|
||||
@IsLowercase()
|
||||
username: Username;
|
||||
@IsString()
|
||||
password: string;
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsLowercase, IsString } from 'class-validator';
|
||||
|
||||
import { Username } from '../../utils/username';
|
||||
|
||||
export class RegisterDto {
|
||||
@Type(() => String)
|
||||
@IsString()
|
||||
@IsLowercase()
|
||||
username: Username;
|
||||
|
||||
@IsString()
|
||||
displayName: string;
|
||||
|
||||
@IsString()
|
||||
password: string;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { IsString } from 'class-validator';
|
||||
|
||||
export class UpdatePasswordDto {
|
||||
@IsString()
|
||||
currentPassword: string;
|
||||
@IsString()
|
||||
newPassword: string;
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { FullUserInfoDto, ProviderType } from '@hedgedoc/commons';
|
||||
import {
|
||||
ForbiddenException,
|
||||
Inject,
|
||||
|
@ -20,10 +21,8 @@ import authConfiguration, {
|
|||
} from '../../config/auth.config';
|
||||
import { NotInDBError } from '../../errors/errors';
|
||||
import { ConsoleLoggerService } from '../../logger/console-logger.service';
|
||||
import { FullUserInfoDto } from '../../users/user-info.dto';
|
||||
import { Identity } from '../identity.entity';
|
||||
import { IdentityService } from '../identity.service';
|
||||
import { ProviderType } from '../provider-type.enum';
|
||||
import { RequestWithSession } from '../session.guard';
|
||||
|
||||
interface OidcClientConfigEntry {
|
||||
|
@ -227,8 +226,8 @@ export class OidcService {
|
|||
const newUserData = {
|
||||
username,
|
||||
displayName,
|
||||
photoUrl,
|
||||
email,
|
||||
photoUrl: photoUrl ?? null,
|
||||
email: email ?? null,
|
||||
};
|
||||
request.session.providerUserId = userId;
|
||||
request.session.newUserData = newUserData;
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { IsOptional, IsString } from 'class-validator';
|
||||
|
||||
import { BaseDto } from '../utils/base.dto.';
|
||||
|
||||
export class PendingUserConfirmationDto extends BaseDto {
|
||||
@IsString()
|
||||
username: string;
|
||||
|
||||
@IsString()
|
||||
displayName: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
profilePicture: string | undefined;
|
||||
}
|
|
@ -1,8 +1,10 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ProviderType } from '@hedgedoc/commons';
|
||||
import { GuestAccess } from '@hedgedoc/commons';
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
|
@ -13,13 +15,11 @@ import {
|
|||
import { Request } from 'express';
|
||||
|
||||
import { CompleteRequest } from '../api/utils/request.type';
|
||||
import { GuestAccess } from '../config/guest_access.enum';
|
||||
import noteConfiguration, { NoteConfig } from '../config/note.config';
|
||||
import { NotInDBError } from '../errors/errors';
|
||||
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
||||
import { SessionState } from '../sessions/session.service';
|
||||
import { UsersService } from '../users/users.service';
|
||||
import { ProviderType } from './provider-type.enum';
|
||||
|
||||
export type RequestWithSession = Request & {
|
||||
session: SessionState;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -10,24 +10,24 @@ import { buildErrorMessage } from './utils';
|
|||
|
||||
export interface CustomizationConfig {
|
||||
branding: {
|
||||
customName: string;
|
||||
customLogo: string;
|
||||
customName: string | null;
|
||||
customLogo: string | null;
|
||||
};
|
||||
specialUrls: {
|
||||
privacy: string;
|
||||
termsOfUse: string;
|
||||
imprint: string;
|
||||
privacy: string | null;
|
||||
termsOfUse: string | null;
|
||||
imprint: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
const schema = Joi.object({
|
||||
branding: Joi.object({
|
||||
customName: Joi.string().optional().label('HD_CUSTOM_NAME'),
|
||||
customName: Joi.string().allow(null).label('HD_CUSTOM_NAME'),
|
||||
customLogo: Joi.string()
|
||||
.uri({
|
||||
scheme: [/https?/],
|
||||
})
|
||||
.optional()
|
||||
.allow(null)
|
||||
.label('HD_CUSTOM_LOGO'),
|
||||
}),
|
||||
specialUrls: Joi.object({
|
||||
|
@ -35,19 +35,19 @@ const schema = Joi.object({
|
|||
.uri({
|
||||
scheme: /https?/,
|
||||
})
|
||||
.optional()
|
||||
.allow(null)
|
||||
.label('HD_PRIVACY_URL'),
|
||||
termsOfUse: Joi.string()
|
||||
.uri({
|
||||
scheme: /https?/,
|
||||
})
|
||||
.optional()
|
||||
.allow(null)
|
||||
.label('HD_TERMS_OF_USE_URL'),
|
||||
imprint: Joi.string()
|
||||
.uri({
|
||||
scheme: /https?/,
|
||||
})
|
||||
.optional()
|
||||
.allow(null)
|
||||
.label('HD_IMPRINT_URL'),
|
||||
}),
|
||||
});
|
||||
|
@ -56,13 +56,13 @@ export default registerAs('customizationConfig', () => {
|
|||
const customizationConfig = schema.validate(
|
||||
{
|
||||
branding: {
|
||||
customName: process.env.HD_CUSTOM_NAME,
|
||||
customLogo: process.env.HD_CUSTOM_LOGO,
|
||||
customName: process.env.HD_CUSTOM_NAME ?? null,
|
||||
customLogo: process.env.HD_CUSTOM_LOGO ?? null,
|
||||
},
|
||||
specialUrls: {
|
||||
privacy: process.env.HD_PRIVACY_URL,
|
||||
termsOfUse: process.env.HD_TERMS_OF_USE_URL,
|
||||
imprint: process.env.HD_IMPRINT_URL,
|
||||
privacy: process.env.HD_PRIVACY_URL ?? null,
|
||||
termsOfUse: process.env.HD_TERMS_OF_USE_URL ?? null,
|
||||
imprint: process.env.HD_IMPRINT_URL ?? null,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -9,7 +9,7 @@ import * as Joi from 'joi';
|
|||
import { buildErrorMessage } from './utils';
|
||||
|
||||
export interface ExternalServicesConfig {
|
||||
plantUmlServer: string;
|
||||
plantUmlServer: string | null;
|
||||
imageProxy: string;
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ const schema = Joi.object({
|
|||
.uri({
|
||||
scheme: /https?/,
|
||||
})
|
||||
.optional()
|
||||
.allow(null)
|
||||
.label('HD_PLANTUML_SERVER'),
|
||||
imageProxy: Joi.string()
|
||||
.uri({
|
||||
|
@ -36,7 +36,7 @@ export default registerAs('externalServicesConfig', () => {
|
|||
}
|
||||
const externalConfig = schema.validate(
|
||||
{
|
||||
plantUmlServer: process.env.HD_PLANTUML_SERVER,
|
||||
plantUmlServer: process.env.HD_PLANTUML_SERVER ?? null,
|
||||
imageProxy: process.env.HD_IMAGE_PROXY,
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { GuestAccess } from '@hedgedoc/commons';
|
||||
import { ConfigFactoryKeyHost, registerAs } from '@nestjs/config';
|
||||
import { ConfigFactory } from '@nestjs/config/dist/interfaces';
|
||||
|
||||
import { DefaultAccessLevel } from '../default-access-level.enum';
|
||||
import { GuestAccess } from '../guest_access.enum';
|
||||
import { NoteConfig } from '../note.config';
|
||||
|
||||
export function createDefaultMockNoteConfig(): NoteConfig {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { GuestAccess } from '@hedgedoc/commons';
|
||||
import mockedEnv from 'mocked-env';
|
||||
|
||||
import { DefaultAccessLevel } from './default-access-level.enum';
|
||||
import { GuestAccess } from './guest_access.enum';
|
||||
import noteConfig from './note.config';
|
||||
|
||||
describe('noteConfig', () => {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { GuestAccess } from '@hedgedoc/commons';
|
||||
import { registerAs } from '@nestjs/config';
|
||||
import * as Joi from 'joi';
|
||||
|
||||
|
@ -10,7 +11,6 @@ import {
|
|||
DefaultAccessLevel,
|
||||
getDefaultAccessLevelOrdinal,
|
||||
} from './default-access-level.enum';
|
||||
import { GuestAccess } from './guest_access.enum';
|
||||
import { buildErrorMessage, parseOptionalNumber, toArrayConfig } from './utils';
|
||||
|
||||
export interface NoteConfig {
|
||||
|
|
|
@ -1,194 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { Type } from 'class-transformer';
|
||||
import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUrl,
|
||||
ValidateNested,
|
||||
} from 'class-validator';
|
||||
import { URL } from 'url';
|
||||
|
||||
import { ProviderType } from '../auth/provider-type.enum';
|
||||
import { GuestAccess } from '../config/guest_access.enum';
|
||||
import { ServerVersion } from '../monitoring/server-status.dto';
|
||||
import { BaseDto } from '../utils/base.dto.';
|
||||
|
||||
export type AuthProviderTypeWithCustomName =
|
||||
| ProviderType.LDAP
|
||||
| ProviderType.OIDC;
|
||||
|
||||
export type AuthProviderTypeWithoutCustomName = ProviderType.LOCAL;
|
||||
|
||||
export class AuthProviderWithoutCustomNameDto extends BaseDto {
|
||||
/**
|
||||
* The type of the auth provider.
|
||||
*/
|
||||
@IsString()
|
||||
@Type(() => String)
|
||||
type: AuthProviderTypeWithoutCustomName;
|
||||
}
|
||||
|
||||
export class AuthProviderWithCustomNameDto extends BaseDto {
|
||||
/**
|
||||
* The type of the auth provider.
|
||||
*/
|
||||
@IsString()
|
||||
@Type(() => String)
|
||||
type: AuthProviderTypeWithCustomName;
|
||||
|
||||
/**
|
||||
* The identifier with which the auth provider can be called
|
||||
* @example gitlab-fsorg
|
||||
*/
|
||||
@IsString()
|
||||
identifier: string;
|
||||
|
||||
/**
|
||||
* The name given to the auth provider
|
||||
* @example GitLab fachschaften.org
|
||||
*/
|
||||
@IsString()
|
||||
providerName: string;
|
||||
|
||||
/**
|
||||
* The theme to apply for the login button.
|
||||
* @example gitlab
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
theme?: string;
|
||||
}
|
||||
|
||||
export type AuthProviderDto =
|
||||
| AuthProviderWithCustomNameDto
|
||||
| AuthProviderWithoutCustomNameDto;
|
||||
|
||||
export class BrandingDto extends BaseDto {
|
||||
/**
|
||||
* The name to be displayed next to the HedgeDoc logo
|
||||
* @example ACME Corp
|
||||
*/
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* The logo to be displayed next to the HedgeDoc logo
|
||||
* @example https://md.example.com/logo.png
|
||||
*/
|
||||
@IsUrl()
|
||||
@IsOptional()
|
||||
@Type(() => URL)
|
||||
logo?: URL;
|
||||
}
|
||||
|
||||
export class SpecialUrlsDto extends BaseDto {
|
||||
/**
|
||||
* A link to the privacy notice
|
||||
* @example https://md.example.com/n/privacy
|
||||
*/
|
||||
@IsUrl()
|
||||
@IsOptional()
|
||||
@Type(() => URL)
|
||||
privacy?: URL;
|
||||
|
||||
/**
|
||||
* A link to the terms of use
|
||||
* @example https://md.example.com/n/termsOfUse
|
||||
*/
|
||||
@IsUrl()
|
||||
@IsOptional()
|
||||
@Type(() => URL)
|
||||
termsOfUse?: URL;
|
||||
|
||||
/**
|
||||
* A link to the imprint
|
||||
* @example https://md.example.com/n/imprint
|
||||
*/
|
||||
@IsUrl()
|
||||
@IsOptional()
|
||||
@Type(() => URL)
|
||||
imprint?: URL;
|
||||
}
|
||||
|
||||
export class FrontendConfigDto extends BaseDto {
|
||||
/**
|
||||
* Maximum access level for guest users
|
||||
*/
|
||||
@IsString()
|
||||
guestAccess: GuestAccess;
|
||||
|
||||
/**
|
||||
* Are users allowed to register on this instance?
|
||||
*/
|
||||
@IsBoolean()
|
||||
allowRegister: boolean;
|
||||
|
||||
/**
|
||||
* Are users allowed to edit their profile information?
|
||||
*/
|
||||
@IsBoolean()
|
||||
allowProfileEdits: boolean;
|
||||
|
||||
/**
|
||||
* Are users allowed to choose their username when signing up via OIDC?
|
||||
*/
|
||||
@IsBoolean()
|
||||
allowChooseUsername: boolean;
|
||||
|
||||
/**
|
||||
* Which auth providers are enabled and how are they configured?
|
||||
*/
|
||||
// eslint-disable-next-line @darraghor/nestjs-typed/validated-non-primitive-property-needs-type-decorator
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
authProviders: AuthProviderDto[];
|
||||
|
||||
/**
|
||||
* Individual branding information
|
||||
*/
|
||||
@ValidateNested()
|
||||
@Type(() => BrandingDto)
|
||||
branding: BrandingDto;
|
||||
|
||||
/**
|
||||
* Is an image proxy enabled?
|
||||
*/
|
||||
@IsBoolean()
|
||||
useImageProxy: boolean;
|
||||
|
||||
/**
|
||||
* Links to some special pages
|
||||
*/
|
||||
@ValidateNested()
|
||||
@Type(() => SpecialUrlsDto)
|
||||
specialUrls: SpecialUrlsDto;
|
||||
|
||||
/**
|
||||
* The version of HedgeDoc
|
||||
*/
|
||||
@ValidateNested()
|
||||
@Type(() => ServerVersion)
|
||||
version: ServerVersion;
|
||||
|
||||
/**
|
||||
* The plantUML server that should be used to render.
|
||||
*/
|
||||
@IsUrl()
|
||||
@IsOptional()
|
||||
@Type(() => URL)
|
||||
plantUmlServer?: URL;
|
||||
|
||||
/**
|
||||
* The maximal length of each document
|
||||
*/
|
||||
@IsNumber()
|
||||
maxDocumentLength: number;
|
||||
}
|
|
@ -1,19 +1,18 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { GuestAccess, ProviderType } from '@hedgedoc/commons';
|
||||
import { ConfigModule, registerAs } from '@nestjs/config';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { URL } from 'url';
|
||||
|
||||
import { ProviderType } from '../auth/provider-type.enum';
|
||||
import { AppConfig } from '../config/app.config';
|
||||
import { AuthConfig } from '../config/auth.config';
|
||||
import { CustomizationConfig } from '../config/customization.config';
|
||||
import { DefaultAccessLevel } from '../config/default-access-level.enum';
|
||||
import { ExternalServicesConfig } from '../config/external-services.config';
|
||||
import { GuestAccess } from '../config/guest_access.enum';
|
||||
import { Loglevel } from '../config/loglevel.enum';
|
||||
import { NoteConfig } from '../config/note.config';
|
||||
import { LoggerModule } from '../logger/logger.module';
|
||||
|
@ -174,14 +173,11 @@ describe('FrontendConfigService', () => {
|
|||
const customName = 'Test Branding Name';
|
||||
|
||||
let index = 1;
|
||||
for (const customLogo of [undefined, 'https://example.com/logo.png']) {
|
||||
for (const privacyLink of [undefined, 'https://example.com/privacy']) {
|
||||
for (const termsOfUseLink of [undefined, 'https://example.com/terms']) {
|
||||
for (const imprintLink of [undefined, 'https://example.com/imprint']) {
|
||||
for (const plantUmlServer of [
|
||||
undefined,
|
||||
'https://plantuml.example.com',
|
||||
]) {
|
||||
for (const customLogo of [null, 'https://example.com/logo.png']) {
|
||||
for (const privacyLink of [null, 'https://example.com/privacy']) {
|
||||
for (const termsOfUseLink of [null, 'https://example.com/terms']) {
|
||||
for (const imprintLink of [null, 'https://example.com/imprint']) {
|
||||
for (const plantUmlServer of [null, 'https://plantuml.example.com']) {
|
||||
it(`combination #${index} works`, async () => {
|
||||
const appConfig: AppConfig = {
|
||||
baseUrl: domain,
|
||||
|
@ -255,20 +251,24 @@ describe('FrontendConfigService', () => {
|
|||
expect(config.guestAccess).toEqual(noteConfig.guestAccess);
|
||||
expect(config.branding.name).toEqual(customName);
|
||||
expect(config.branding.logo).toEqual(
|
||||
customLogo ? new URL(customLogo) : undefined,
|
||||
customLogo !== null ? new URL(customLogo).toString() : null,
|
||||
);
|
||||
expect(config.maxDocumentLength).toEqual(maxDocumentLength);
|
||||
expect(config.plantUmlServer).toEqual(
|
||||
plantUmlServer ? new URL(plantUmlServer) : undefined,
|
||||
plantUmlServer !== null
|
||||
? new URL(plantUmlServer).toString()
|
||||
: null,
|
||||
);
|
||||
expect(config.specialUrls.imprint).toEqual(
|
||||
imprintLink ? new URL(imprintLink) : undefined,
|
||||
imprintLink !== null ? new URL(imprintLink).toString() : null,
|
||||
);
|
||||
expect(config.specialUrls.privacy).toEqual(
|
||||
privacyLink ? new URL(privacyLink) : undefined,
|
||||
privacyLink !== null ? new URL(privacyLink).toString() : null,
|
||||
);
|
||||
expect(config.specialUrls.termsOfUse).toEqual(
|
||||
termsOfUseLink ? new URL(termsOfUseLink) : undefined,
|
||||
termsOfUseLink !== null
|
||||
? new URL(termsOfUseLink).toString()
|
||||
: null,
|
||||
);
|
||||
expect(config.useImageProxy).toEqual(!!imageProxy);
|
||||
expect(config.version).toEqual(
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import {
|
||||
BrandingDto,
|
||||
FrontendConfigDto,
|
||||
ProviderType,
|
||||
SpecialUrlDto,
|
||||
} from '@hedgedoc/commons';
|
||||
import { AuthProviderDto } from '@hedgedoc/commons';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { URL } from 'url';
|
||||
|
||||
import { ProviderType } from '../auth/provider-type.enum';
|
||||
import appConfiguration, { AppConfig } from '../config/app.config';
|
||||
import authConfiguration, { AuthConfig } from '../config/auth.config';
|
||||
import customizationConfiguration, {
|
||||
|
@ -18,12 +24,6 @@ import externalServicesConfiguration, {
|
|||
import noteConfiguration, { NoteConfig } from '../config/note.config';
|
||||
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
||||
import { getServerVersionFromPackageJson } from '../utils/serverVersion';
|
||||
import {
|
||||
AuthProviderDto,
|
||||
BrandingDto,
|
||||
FrontendConfigDto,
|
||||
SpecialUrlsDto,
|
||||
} from './frontend-config.dto';
|
||||
|
||||
@Injectable()
|
||||
export class FrontendConfigService {
|
||||
|
@ -53,8 +53,8 @@ export class FrontendConfigService {
|
|||
branding: this.getBranding(),
|
||||
maxDocumentLength: this.noteConfig.maxDocumentLength,
|
||||
plantUmlServer: this.externalServicesConfig.plantUmlServer
|
||||
? new URL(this.externalServicesConfig.plantUmlServer)
|
||||
: undefined,
|
||||
? new URL(this.externalServicesConfig.plantUmlServer).toString()
|
||||
: null,
|
||||
specialUrls: this.getSpecialUrls(),
|
||||
useImageProxy: !!this.externalServicesConfig.imageProxy,
|
||||
version: await getServerVersionFromPackageJson(),
|
||||
|
@ -73,6 +73,7 @@ export class FrontendConfigService {
|
|||
type: ProviderType.LDAP,
|
||||
providerName: ldapEntry.providerName,
|
||||
identifier: ldapEntry.identifier,
|
||||
theme: null,
|
||||
});
|
||||
});
|
||||
this.authConfig.oidc.forEach((openidConnectEntry) => {
|
||||
|
@ -80,7 +81,7 @@ export class FrontendConfigService {
|
|||
type: ProviderType.OIDC,
|
||||
providerName: openidConnectEntry.providerName,
|
||||
identifier: openidConnectEntry.identifier,
|
||||
theme: openidConnectEntry.theme,
|
||||
theme: openidConnectEntry.theme ?? null,
|
||||
});
|
||||
});
|
||||
return providers;
|
||||
|
@ -89,23 +90,23 @@ export class FrontendConfigService {
|
|||
private getBranding(): BrandingDto {
|
||||
return {
|
||||
logo: this.customizationConfig.branding.customLogo
|
||||
? new URL(this.customizationConfig.branding.customLogo)
|
||||
: undefined,
|
||||
? new URL(this.customizationConfig.branding.customLogo).toString()
|
||||
: null,
|
||||
name: this.customizationConfig.branding.customName,
|
||||
};
|
||||
}
|
||||
|
||||
private getSpecialUrls(): SpecialUrlsDto {
|
||||
private getSpecialUrls(): SpecialUrlDto {
|
||||
return {
|
||||
imprint: this.customizationConfig.specialUrls.imprint
|
||||
? new URL(this.customizationConfig.specialUrls.imprint)
|
||||
: undefined,
|
||||
? new URL(this.customizationConfig.specialUrls.imprint).toString()
|
||||
: null,
|
||||
privacy: this.customizationConfig.specialUrls.privacy
|
||||
? new URL(this.customizationConfig.specialUrls.privacy)
|
||||
: undefined,
|
||||
? new URL(this.customizationConfig.specialUrls.privacy).toString()
|
||||
: null,
|
||||
termsOfUse: this.customizationConfig.specialUrls.termsOfUse
|
||||
? new URL(this.customizationConfig.specialUrls.termsOfUse)
|
||||
: undefined,
|
||||
? new URL(this.customizationConfig.specialUrls.termsOfUse).toString()
|
||||
: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsBoolean, IsString } from 'class-validator';
|
||||
|
||||
import { BaseDto } from '../utils/base.dto.';
|
||||
|
||||
export class GroupInfoDto extends BaseDto {
|
||||
/**
|
||||
* Name of the group
|
||||
* @example "superheroes"
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Display name of this group
|
||||
* @example "Superheroes"
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
displayName: string;
|
||||
|
||||
/**
|
||||
* True if this group must be specially handled
|
||||
* Used for e.g. "everybody", "all logged in users"
|
||||
* @example false
|
||||
*/
|
||||
@IsBoolean()
|
||||
@ApiProperty()
|
||||
special: boolean;
|
||||
}
|
|
@ -1,15 +1,15 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { GroupInfoDto } from '@hedgedoc/commons';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { AlreadyInDBError, NotInDBError } from '../errors/errors';
|
||||
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
||||
import { GroupInfoDto } from './group-info.dto';
|
||||
import { Group } from './group.entity';
|
||||
import { SpecialGroup } from './groups.special';
|
||||
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsDate, IsLowercase, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
import { BaseDto } from '../utils/base.dto.';
|
||||
import { Username } from '../utils/username';
|
||||
|
||||
export class MediaUploadDto extends BaseDto {
|
||||
/**
|
||||
* The uuid of the media file.
|
||||
* @example "7697582e-0020-4188-9758-2e00207188ca"
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
uuid: string;
|
||||
|
||||
/**
|
||||
* The original filename of the media upload.
|
||||
* @example "example.png"
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
fileName: string;
|
||||
|
||||
/**
|
||||
* The publicId of the note to which the uploaded file is linked to.
|
||||
* @example "b604x5885k9k01bq7tsmawvnp0"
|
||||
*/
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@ApiProperty()
|
||||
noteId: string | null;
|
||||
|
||||
/**
|
||||
* The date when the upload objects was created.
|
||||
* @example "2020-12-01 12:23:34"
|
||||
*/
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
@ApiProperty()
|
||||
createdAt: Date;
|
||||
|
||||
/**
|
||||
* The username of the user which uploaded the media file.
|
||||
* @example "testuser5"
|
||||
*/
|
||||
@IsString()
|
||||
@IsLowercase()
|
||||
@IsOptional()
|
||||
@ApiProperty()
|
||||
username: Username | null;
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { MediaUploadDto } from '@hedgedoc/commons';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { ModuleRef } from '@nestjs/core';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
@ -22,7 +23,6 @@ import { ImgurBackend } from './backends/imgur-backend';
|
|||
import { S3Backend } from './backends/s3-backend';
|
||||
import { WebdavBackend } from './backends/webdav-backend';
|
||||
import { MediaBackend } from './media-backend.interface';
|
||||
import { MediaUploadDto } from './media-upload.dto';
|
||||
import { MediaUpload } from './media-upload.entity';
|
||||
|
||||
@Injectable()
|
||||
|
@ -268,7 +268,7 @@ export class MediaService {
|
|||
uuid: mediaUpload.uuid,
|
||||
fileName: mediaUpload.fileName,
|
||||
noteId: (await mediaUpload.note)?.publicId ?? null,
|
||||
createdAt: mediaUpload.createdAt,
|
||||
createdAt: mediaUpload.createdAt.toISOString(),
|
||||
username: user?.username ?? null,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ServerStatusDto } from '@hedgedoc/commons';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { getServerVersionFromPackageJson } from '../utils/serverVersion';
|
||||
import { ServerStatusDto } from './server-status.dto';
|
||||
|
||||
@Injectable()
|
||||
export class MonitoringService {
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
|
||||
import { BaseDto } from '../utils/base.dto.';
|
||||
|
||||
export class ServerVersion {
|
||||
@ApiProperty()
|
||||
major: number;
|
||||
@ApiProperty()
|
||||
minor: number;
|
||||
@ApiProperty()
|
||||
patch: number;
|
||||
@ApiPropertyOptional()
|
||||
preRelease?: string;
|
||||
@ApiPropertyOptional()
|
||||
commit?: string;
|
||||
@ApiProperty()
|
||||
fullString: string;
|
||||
}
|
||||
|
||||
export class ServerStatusDto extends BaseDto {
|
||||
@ApiProperty()
|
||||
serverVersion: ServerVersion;
|
||||
@ApiProperty()
|
||||
onlineNotes: number;
|
||||
@ApiProperty()
|
||||
onlineUsers: number;
|
||||
@ApiProperty()
|
||||
distinctOnlineUsers: number;
|
||||
@ApiProperty()
|
||||
notesCount: number;
|
||||
@ApiProperty()
|
||||
registeredUsers: number;
|
||||
@ApiProperty()
|
||||
onlineRegisteredUsers: number;
|
||||
@ApiProperty()
|
||||
distinctOnlineRegisteredUsers: number;
|
||||
@ApiProperty()
|
||||
isConnectionBusy: boolean;
|
||||
@ApiProperty()
|
||||
connectionSocketQueueLength: number;
|
||||
@ApiProperty()
|
||||
isDisconnectBusy: boolean;
|
||||
@ApiProperty()
|
||||
disconnectSocketQueueLength: number;
|
||||
}
|
|
@ -12,7 +12,7 @@ exports[`NotesService toNoteDto works 1`] = `
|
|||
"primaryAlias": true,
|
||||
},
|
||||
],
|
||||
"createdAt": undefined,
|
||||
"createdAt": "2019-02-04T20:34:12.000Z",
|
||||
"description": "mockDescription",
|
||||
"editedBy": [
|
||||
"hardcoded",
|
||||
|
@ -39,7 +39,7 @@ exports[`NotesService toNoteDto works 1`] = `
|
|||
],
|
||||
"title": "mockTitle",
|
||||
"updateUsername": "hardcoded",
|
||||
"updatedAt": 2019-02-04T20:34:12.000Z,
|
||||
"updatedAt": "2019-02-04T20:34:12.000Z",
|
||||
"version": undefined,
|
||||
"viewCount": 1337,
|
||||
},
|
||||
|
@ -55,7 +55,7 @@ exports[`NotesService toNoteMetadataDto works 1`] = `
|
|||
"primaryAlias": true,
|
||||
},
|
||||
],
|
||||
"createdAt": undefined,
|
||||
"createdAt": "2019-02-04T20:34:12.000Z",
|
||||
"description": "mockDescription",
|
||||
"editedBy": [
|
||||
"hardcoded",
|
||||
|
@ -82,7 +82,7 @@ exports[`NotesService toNoteMetadataDto works 1`] = `
|
|||
],
|
||||
"title": "mockTitle",
|
||||
"updateUsername": "hardcoded",
|
||||
"updatedAt": 2019-02-04T20:34:12.000Z,
|
||||
"updatedAt": "2019-02-04T20:34:12.000Z",
|
||||
"version": undefined,
|
||||
"viewCount": 1337,
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString } from 'class-validator';
|
||||
|
||||
import { BaseDto } from '../utils/base.dto.';
|
||||
|
||||
export class AliasCreateDto extends BaseDto {
|
||||
/**
|
||||
* The note id or alias, which identifies the note the alias should be added to
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
noteIdOrAlias: string;
|
||||
|
||||
/**
|
||||
* The new alias
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
newAlias: string;
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsBoolean } from 'class-validator';
|
||||
|
||||
import { BaseDto } from '../utils/base.dto.';
|
||||
|
||||
export class AliasUpdateDto extends BaseDto {
|
||||
/**
|
||||
* Whether the alias should become the primary alias or not
|
||||
*/
|
||||
@IsBoolean()
|
||||
@ApiProperty()
|
||||
primaryAlias: boolean;
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsBoolean, IsString } from 'class-validator';
|
||||
|
||||
import { BaseDto } from '../utils/base.dto.';
|
||||
|
||||
export class AliasDto extends BaseDto {
|
||||
/**
|
||||
* The name of the alias
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Is the alias the primary alias or not
|
||||
*/
|
||||
@IsBoolean()
|
||||
@ApiProperty()
|
||||
primaryAlias: boolean;
|
||||
|
||||
/**
|
||||
* The public id of the note the alias is associated with
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
noteId: string;
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { AliasDto } from '@hedgedoc/commons';
|
||||
import { forwardRef, Inject, Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
|
@ -12,7 +13,6 @@ import {
|
|||
PrimaryAliasDeletionForbiddenError,
|
||||
} from '../errors/errors';
|
||||
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
||||
import { AliasDto } from './alias.dto';
|
||||
import { Alias } from './alias.entity';
|
||||
import { Note } from './note.entity';
|
||||
import { NotesService } from './notes.service';
|
||||
|
|
|
@ -1,158 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import {
|
||||
IsArray,
|
||||
IsDate,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsString,
|
||||
ValidateNested,
|
||||
} from 'class-validator';
|
||||
|
||||
import { BaseDto } from '../utils/base.dto.';
|
||||
import { AliasDto } from './alias.dto';
|
||||
import { NotePermissionsDto } from './note-permissions.dto';
|
||||
|
||||
export class NoteMetadataDto extends BaseDto {
|
||||
/**
|
||||
* ID of the note
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* All aliases of the note (including the primaryAlias)
|
||||
*/
|
||||
@Type(() => AliasDto)
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@ApiProperty({ isArray: true, type: AliasDto })
|
||||
aliases: AliasDto[];
|
||||
|
||||
/**
|
||||
* The primary adress of the note
|
||||
* If at least one alias is set, this is the primary alias
|
||||
* If no alias is set, this is the note's ID
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
primaryAddress: string;
|
||||
|
||||
/**
|
||||
* Title of the note
|
||||
* Does not contain any markup but might be empty
|
||||
* @example "Shopping List"
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
title: string;
|
||||
|
||||
/**
|
||||
* Description of the note
|
||||
* Does not contain any markup but might be empty
|
||||
* @example Everything I want to buy
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
description: string;
|
||||
|
||||
/**
|
||||
* List of tags assigned to this note
|
||||
* @example "['shopping', 'personal']
|
||||
*/
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
@ApiProperty({ isArray: true, type: String })
|
||||
tags: string[];
|
||||
|
||||
@IsNumber()
|
||||
@ApiProperty()
|
||||
version: number;
|
||||
|
||||
/**
|
||||
* Datestring of the last time this note was updated
|
||||
* @example "2020-12-01 12:23:34"
|
||||
*/
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
@ApiProperty()
|
||||
updatedAt: Date;
|
||||
|
||||
/**
|
||||
* User that last edited the note
|
||||
*/
|
||||
// eslint-disable-next-line @darraghor/nestjs-typed/api-property-matches-property-optionality
|
||||
@ApiPropertyOptional()
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
updateUsername: string | null;
|
||||
|
||||
/**
|
||||
* Counts how many times the published note has been viewed
|
||||
* @example 42
|
||||
*/
|
||||
@IsNumber()
|
||||
@ApiProperty()
|
||||
viewCount: number;
|
||||
|
||||
/**
|
||||
* Datestring of the time this note was created
|
||||
* @example "2020-12-01 12:23:34"
|
||||
*/
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
@ApiProperty()
|
||||
createdAt: Date;
|
||||
|
||||
/**
|
||||
* List of usernames that edited the note
|
||||
* @example "['john.smith', 'jane.smith']"
|
||||
*/
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
@ApiProperty({ isArray: true, type: String })
|
||||
editedBy: string[];
|
||||
|
||||
/**
|
||||
* Permissions currently in effect for the note
|
||||
*/
|
||||
@ValidateNested()
|
||||
@Type(() => NotePermissionsDto)
|
||||
@ApiProperty({ type: NotePermissionsDto })
|
||||
permissions: NotePermissionsDto;
|
||||
}
|
||||
|
||||
export class NoteMetadataUpdateDto {
|
||||
/**
|
||||
* New title of the note
|
||||
* Can not contain any markup and might be empty
|
||||
* @example "Shopping List"
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
title: string;
|
||||
|
||||
/**
|
||||
* New description of the note
|
||||
* Can not contain any markup but might be empty
|
||||
* @example Everything I want to buy
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
description: string;
|
||||
|
||||
/**
|
||||
* New list of tags assigned to this note
|
||||
* @example "['shopping', 'personal']
|
||||
*/
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
@ApiProperty({ isArray: true, type: String })
|
||||
tags: string[];
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsLowercase,
|
||||
IsOptional,
|
||||
IsString,
|
||||
ValidateNested,
|
||||
} from 'class-validator';
|
||||
|
||||
import { BaseDto } from '../utils/base.dto.';
|
||||
import { Username } from '../utils/username';
|
||||
|
||||
export class NoteUserPermissionEntryDto extends BaseDto {
|
||||
/**
|
||||
* Username of the User this permission applies to
|
||||
*/
|
||||
@Type(() => String)
|
||||
@IsString()
|
||||
@IsLowercase()
|
||||
@ApiProperty()
|
||||
username: Username;
|
||||
|
||||
/**
|
||||
* True if the user is allowed to edit the note
|
||||
* @example false
|
||||
*/
|
||||
@IsBoolean()
|
||||
@ApiProperty()
|
||||
canEdit: boolean;
|
||||
}
|
||||
|
||||
export class NoteUserPermissionUpdateDto {
|
||||
/**
|
||||
* Username of the user this permission should apply to
|
||||
* @example "john.smith"
|
||||
*/
|
||||
@Type(() => String)
|
||||
@IsString()
|
||||
@IsLowercase()
|
||||
@ApiProperty()
|
||||
username: Username;
|
||||
|
||||
/**
|
||||
* True if the user should be allowed to edit the note
|
||||
* @example false
|
||||
*/
|
||||
@IsBoolean()
|
||||
@ApiProperty()
|
||||
canEdit: boolean;
|
||||
}
|
||||
|
||||
export class NoteGroupPermissionEntryDto {
|
||||
/**
|
||||
* Name of the Group this permission applies to
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
groupName: string;
|
||||
|
||||
/**
|
||||
* True if the group members are allowed to edit the note
|
||||
* @example false
|
||||
*/
|
||||
@IsBoolean()
|
||||
@ApiProperty()
|
||||
canEdit: boolean;
|
||||
}
|
||||
|
||||
export class NoteGroupPermissionUpdateDto {
|
||||
/**
|
||||
* Name of the group this permission should apply to
|
||||
* @example "superheroes"
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
groupName: string;
|
||||
|
||||
/**
|
||||
* True if the group members should be allowed to edit the note
|
||||
* @example false
|
||||
*/
|
||||
@IsBoolean()
|
||||
@ApiProperty()
|
||||
canEdit: boolean;
|
||||
}
|
||||
|
||||
export class NotePermissionsDto {
|
||||
/**
|
||||
* Username of the User this permission applies to
|
||||
*/
|
||||
// nestjs-typed does not detect '| null' types as optional
|
||||
// eslint-disable-next-line @darraghor/nestjs-typed/api-property-matches-property-optionality
|
||||
@Type(() => String)
|
||||
@IsString()
|
||||
@ApiPropertyOptional()
|
||||
@IsOptional()
|
||||
owner: string | null;
|
||||
|
||||
/**
|
||||
* List of users the note is shared with
|
||||
*/
|
||||
@ValidateNested({ each: true })
|
||||
@IsArray()
|
||||
@Type(() => NoteUserPermissionEntryDto)
|
||||
@ApiProperty({ isArray: true, type: NoteUserPermissionEntryDto })
|
||||
sharedToUsers: NoteUserPermissionEntryDto[];
|
||||
|
||||
/**
|
||||
* List of groups the note is shared with
|
||||
*/
|
||||
@ValidateNested({ each: true })
|
||||
@IsArray()
|
||||
@Type(() => NoteGroupPermissionEntryDto)
|
||||
@ApiProperty({ isArray: true, type: NoteGroupPermissionEntryDto })
|
||||
sharedToGroups: NoteGroupPermissionEntryDto[];
|
||||
}
|
||||
|
||||
export class NotePermissionsUpdateDto {
|
||||
/**
|
||||
* List of users the note should be shared with
|
||||
*/
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => NoteUserPermissionUpdateDto)
|
||||
@ApiProperty({ isArray: true, type: NoteUserPermissionUpdateDto })
|
||||
sharedToUsers: NoteUserPermissionUpdateDto[];
|
||||
|
||||
/**
|
||||
* List of groups the note should be shared with
|
||||
*/
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => NoteGroupPermissionUpdateDto)
|
||||
@ApiProperty({ isArray: true, type: NoteGroupPermissionUpdateDto })
|
||||
sharedToGroups: NoteGroupPermissionUpdateDto[];
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsArray, IsString, ValidateNested } from 'class-validator';
|
||||
|
||||
import { EditDto } from '../revisions/edit.dto';
|
||||
import { BaseDto } from '../utils/base.dto.';
|
||||
import { NoteMetadataDto } from './note-metadata.dto';
|
||||
|
||||
export class NoteDto extends BaseDto {
|
||||
/**
|
||||
* Markdown content of the note
|
||||
* @example "# I am a heading"
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
content: string;
|
||||
|
||||
/**
|
||||
* Metadata of the note
|
||||
*/
|
||||
@Type(() => NoteMetadataDto)
|
||||
@ValidateNested()
|
||||
@ApiProperty({ type: NoteMetadataDto })
|
||||
metadata: NoteMetadataDto;
|
||||
|
||||
/**
|
||||
* Edit information of this note
|
||||
*/
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => EditDto)
|
||||
@ApiProperty({ isArray: true, type: EditDto })
|
||||
editedByAtPosition: EditDto[];
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsBoolean } from 'class-validator';
|
||||
|
||||
import { BaseDto } from '../utils/base.dto.';
|
||||
|
||||
export class NoteMediaDeletionDto extends BaseDto {
|
||||
/**
|
||||
* Should the associated mediaUploads be keept
|
||||
* @default false
|
||||
* @example false
|
||||
*/
|
||||
@IsBoolean()
|
||||
@ApiProperty()
|
||||
keepMedia: boolean;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -271,6 +271,7 @@ describe('NotesService', () => {
|
|||
const note = Mock.of<Note>({
|
||||
revisions: Promise.resolve([revision]),
|
||||
aliases: Promise.resolve([]),
|
||||
createdAt: new Date(1549312452000),
|
||||
});
|
||||
|
||||
mockRevisionService(note, revision);
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import {
|
||||
NoteDto,
|
||||
NoteMetadataDto,
|
||||
NotePermissionsDto,
|
||||
} from '@hedgedoc/commons';
|
||||
import { Optional } from '@mrdrogdrog/optional';
|
||||
import { forwardRef, Inject, Injectable } from '@nestjs/common';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
|
@ -30,9 +35,6 @@ import { User } from '../users/user.entity';
|
|||
import { UsersService } from '../users/users.service';
|
||||
import { Alias } from './alias.entity';
|
||||
import { AliasService } from './alias.service';
|
||||
import { NoteMetadataDto } from './note-metadata.dto';
|
||||
import { NotePermissionsDto } from './note-permissions.dto';
|
||||
import { NoteDto } from './note.dto';
|
||||
import { Note } from './note.entity';
|
||||
import { Tag } from './tag.entity';
|
||||
import { getPrimaryAlias } from './utils';
|
||||
|
@ -427,11 +429,11 @@ export class NotesService {
|
|||
title: latestRevision.title,
|
||||
description: latestRevision.description,
|
||||
tags: (await latestRevision.tags).map((tag) => tag.name),
|
||||
createdAt: note.createdAt,
|
||||
createdAt: note.createdAt.toISOString(),
|
||||
editedBy: (await this.getAuthorUsers(note)).map((user) => user.username),
|
||||
permissions: await this.toNotePermissionsDto(note),
|
||||
version: note.version,
|
||||
updatedAt: latestRevision.createdAt,
|
||||
updatedAt: latestRevision.createdAt.toISOString(),
|
||||
updateUsername: updateUser ? updateUser.username : null,
|
||||
viewCount: note.viewCount,
|
||||
};
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import {
|
||||
GuestAccess,
|
||||
NoteGroupPermissionUpdateDto,
|
||||
NoteUserPermissionUpdateDto,
|
||||
} from '@hedgedoc/commons';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { EventEmitter2, EventEmitterModule } from '@nestjs/event-emitter';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
@ -14,7 +19,6 @@ import { ApiToken } from '../api-token/api-token.entity';
|
|||
import { Identity } from '../auth/identity.entity';
|
||||
import { Author } from '../authors/author.entity';
|
||||
import { DefaultAccessLevel } from '../config/default-access-level.enum';
|
||||
import { GuestAccess } from '../config/guest_access.enum';
|
||||
import appConfigMock from '../config/mock/app.config.mock';
|
||||
import authConfigMock from '../config/mock/auth.config.mock';
|
||||
import databaseConfigMock from '../config/mock/database.config.mock';
|
||||
|
@ -31,10 +35,6 @@ import { GroupsService } from '../groups/groups.service';
|
|||
import { LoggerModule } from '../logger/logger.module';
|
||||
import { MediaUpload } from '../media/media-upload.entity';
|
||||
import { Alias } from '../notes/alias.entity';
|
||||
import {
|
||||
NoteGroupPermissionUpdateDto,
|
||||
NoteUserPermissionUpdateDto,
|
||||
} from '../notes/note-permissions.dto';
|
||||
import { Note } from '../notes/note.entity';
|
||||
import { NotesModule } from '../notes/notes.module';
|
||||
import { Tag } from '../notes/tag.entity';
|
||||
|
@ -461,12 +461,15 @@ describe('PermissionsService', () => {
|
|||
});
|
||||
|
||||
describe('updateNotePermissions', () => {
|
||||
const userPermissionUpdate = new NoteUserPermissionUpdateDto();
|
||||
userPermissionUpdate.username = 'hardcoded';
|
||||
userPermissionUpdate.canEdit = true;
|
||||
const groupPermissionUpdate = new NoteGroupPermissionUpdateDto();
|
||||
groupPermissionUpdate.groupName = 'testGroup';
|
||||
groupPermissionUpdate.canEdit = false;
|
||||
const userPermissionUpdate: NoteUserPermissionUpdateDto = {
|
||||
username: 'hardcoded',
|
||||
canEdit: true,
|
||||
};
|
||||
|
||||
const groupPermissionUpdate: NoteGroupPermissionUpdateDto = {
|
||||
groupName: 'testGroup',
|
||||
canEdit: false,
|
||||
};
|
||||
const user = User.create(userPermissionUpdate.username, 'Testy') as User;
|
||||
const group = Group.create(
|
||||
groupPermissionUpdate.groupName,
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { GuestAccess, NotePermissionsUpdateDto } from '@hedgedoc/commons';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { GuestAccess } from '../config/guest_access.enum';
|
||||
import noteConfiguration, { NoteConfig } from '../config/note.config';
|
||||
import { PermissionsUpdateInconsistentError } from '../errors/errors';
|
||||
import { NoteEvent, NoteEventMap } from '../events';
|
||||
|
@ -16,7 +16,6 @@ import { Group } from '../groups/group.entity';
|
|||
import { GroupsService } from '../groups/groups.service';
|
||||
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
||||
import { MediaUpload } from '../media/media-upload.entity';
|
||||
import { NotePermissionsUpdateDto } from '../notes/note-permissions.dto';
|
||||
import { Note } from '../notes/note.entity';
|
||||
import { User } from '../users/user.entity';
|
||||
import { UsersService } from '../users/users.service';
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { GuestAccess } from '../../config/guest_access.enum';
|
||||
import { GuestAccess } from '@hedgedoc/commons';
|
||||
|
||||
import { NotePermission } from '../note-permission.enum';
|
||||
import { convertGuestAccessToNotePermission } from './convert-guest-access-to-note-permission';
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { GuestAccess } from '../../config/guest_access.enum';
|
||||
import { GuestAccess } from '@hedgedoc/commons';
|
||||
|
||||
import { NotePermission } from '../note-permission.enum';
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -13,7 +13,6 @@ import { Mock } from 'ts-mockery';
|
|||
|
||||
import { Note } from '../../notes/note.entity';
|
||||
import { User } from '../../users/user.entity';
|
||||
import { Username } from '../../utils/username';
|
||||
import * as NameRandomizerModule from './random-word-lists/name-randomizer';
|
||||
import { RealtimeConnection } from './realtime-connection';
|
||||
import { RealtimeNote } from './realtime-note';
|
||||
|
@ -40,7 +39,7 @@ describe('websocket connection', () => {
|
|||
let mockedUser: User;
|
||||
let mockedMessageTransporter: MessageTransporter;
|
||||
|
||||
const mockedUserName: Username = 'mocked-user-name';
|
||||
const mockedUserName: string = 'mocked-user-name';
|
||||
const mockedDisplayName = 'mockedDisplayName';
|
||||
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -11,8 +11,6 @@ import {
|
|||
} from '@hedgedoc/commons';
|
||||
import { Listener } from 'eventemitter2';
|
||||
|
||||
import { Username } from '../../utils/username';
|
||||
|
||||
export type OtherAdapterCollector = () => RealtimeUserStatusAdapter[];
|
||||
|
||||
/**
|
||||
|
@ -22,7 +20,7 @@ export class RealtimeUserStatusAdapter {
|
|||
private readonly realtimeUser: RealtimeUser;
|
||||
|
||||
constructor(
|
||||
private readonly username: Username | null,
|
||||
private readonly username: string | null,
|
||||
private readonly displayName: string,
|
||||
private collectOtherAdapters: OtherAdapterCollector,
|
||||
private messageTransporter: MessageTransporter,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -10,7 +10,6 @@ import {
|
|||
import { Mock } from 'ts-mockery';
|
||||
|
||||
import { User } from '../../../users/user.entity';
|
||||
import { Username } from '../../../utils/username';
|
||||
import { RealtimeConnection } from '../realtime-connection';
|
||||
import { RealtimeNote } from '../realtime-note';
|
||||
import { RealtimeUserStatusAdapter } from '../realtime-user-status-adapter';
|
||||
|
@ -22,13 +21,13 @@ enum RealtimeUserState {
|
|||
WITH_READONLY,
|
||||
}
|
||||
|
||||
const MOCK_FALLBACK_USERNAME: Username = 'mock';
|
||||
const MOCK_FALLBACK_USERNAME: string = 'mock';
|
||||
|
||||
/**
|
||||
* Creates a mocked {@link RealtimeConnection realtime connection}.
|
||||
*/
|
||||
export class MockConnectionBuilder {
|
||||
private username: Username | null;
|
||||
private username: string | null;
|
||||
private displayName: string | undefined;
|
||||
private includeRealtimeUserStatus: RealtimeUserState =
|
||||
RealtimeUserState.WITHOUT;
|
||||
|
@ -51,7 +50,7 @@ export class MockConnectionBuilder {
|
|||
*
|
||||
* @param username the username of the mocked user. If this value is omitted then the builder will user a {@link MOCK_FALLBACK_USERNAME fallback}.
|
||||
*/
|
||||
public withLoggedInUser(username?: Username): this {
|
||||
public withLoggedInUser(username?: string): this {
|
||||
const newUsername = username ?? MOCK_FALLBACK_USERNAME;
|
||||
this.username = newUsername;
|
||||
this.displayName = newUsername;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -41,7 +41,6 @@ import { SessionService } from '../../sessions/session.service';
|
|||
import { User } from '../../users/user.entity';
|
||||
import { UsersModule } from '../../users/users.module';
|
||||
import { UsersService } from '../../users/users.service';
|
||||
import { Username } from '../../utils/username';
|
||||
import * as websocketConnectionModule from '../realtime-note/realtime-connection';
|
||||
import { RealtimeConnection } from '../realtime-note/realtime-connection';
|
||||
import { RealtimeNote } from '../realtime-note/realtime-note';
|
||||
|
@ -166,7 +165,7 @@ describe('Websocket gateway', () => {
|
|||
),
|
||||
);
|
||||
|
||||
const mockUsername: Username = 'mock-username';
|
||||
const mockUsername: string = 'mock-username';
|
||||
jest
|
||||
.spyOn(sessionService, 'fetchUsernameForSessionId')
|
||||
.mockImplementation((sessionId: string) =>
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
import {
|
||||
DisconnectReason,
|
||||
MessageTransporter,
|
||||
NotePermissions,
|
||||
userCanEdit,
|
||||
} from '@hedgedoc/commons';
|
||||
import { OnGatewayConnection, WebSocketGateway } from '@nestjs/websockets';
|
||||
|
@ -92,10 +91,7 @@ export class WebsocketGateway implements OnGatewayConnection {
|
|||
);
|
||||
|
||||
const permissions = await this.noteService.toNotePermissionsDto(note);
|
||||
const acceptEdits: boolean = userCanEdit(
|
||||
permissions as NotePermissions,
|
||||
user?.username,
|
||||
);
|
||||
const acceptEdits: boolean = userCanEdit(permissions, user?.username);
|
||||
|
||||
const connection = new RealtimeConnection(
|
||||
websocketTransporter,
|
||||
|
|
|
@ -77,14 +77,14 @@ exports[`RevisionsService toRevisionDto converts a revision 1`] = `
|
|||
"mockusername",
|
||||
],
|
||||
"content": "mockContent",
|
||||
"createdAt": 2020-05-20T09:58:00.000Z,
|
||||
"createdAt": "2020-05-20T09:58:00.000Z",
|
||||
"description": "mockDescription",
|
||||
"edits": [
|
||||
{
|
||||
"createdAt": 2020-03-04T22:32:00.000Z,
|
||||
"endPos": 93,
|
||||
"startPos": 34,
|
||||
"updatedAt": 2021-02-10T12:23:00.000Z,
|
||||
"createdAt": "2020-03-04T22:32:00.000Z",
|
||||
"endPosition": 93,
|
||||
"startPosition": 34,
|
||||
"updatedAt": "2021-02-10T12:23:00.000Z",
|
||||
"username": "mockusername",
|
||||
},
|
||||
],
|
||||
|
@ -104,7 +104,7 @@ exports[`RevisionsService toRevisionMetadataDto converts a revision 1`] = `
|
|||
"authorUsernames": [
|
||||
"mockusername",
|
||||
],
|
||||
"createdAt": 2020-05-20T09:58:00.000Z,
|
||||
"createdAt": "2020-05-20T09:58:00.000Z",
|
||||
"description": "mockDescription",
|
||||
"id": 3246,
|
||||
"length": 1854,
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsDate, IsNumber, IsOptional, IsString, Min } from 'class-validator';
|
||||
|
||||
import { UserInfoDto } from '../users/user-info.dto';
|
||||
import { BaseDto } from '../utils/base.dto.';
|
||||
|
||||
export class EditDto extends BaseDto {
|
||||
/**
|
||||
* Username of the user who authored this section
|
||||
* Is `null` if the user is anonymous
|
||||
* @example "john.smith"
|
||||
*/
|
||||
// nestjs-typed does not detect '| null' types as optional
|
||||
// eslint-disable-next-line @darraghor/nestjs-typed/api-property-matches-property-optionality
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@ApiPropertyOptional()
|
||||
username: UserInfoDto['username'] | null;
|
||||
|
||||
/**
|
||||
* Character index of the start of this section
|
||||
* @example 102
|
||||
*/
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
@ApiProperty()
|
||||
startPos: number;
|
||||
|
||||
/**
|
||||
* Character index of the end of this section
|
||||
* Must be greater than {@link startPos}
|
||||
* @example 154
|
||||
*/
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
@ApiProperty()
|
||||
endPos: number;
|
||||
|
||||
/**
|
||||
* Datestring of the time this section was created
|
||||
* @example "2020-12-01 12:23:34"
|
||||
*/
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
@ApiProperty()
|
||||
createdAt: Date;
|
||||
|
||||
/**
|
||||
* Datestring of the last time this section was edited
|
||||
* @example "2020-12-01 12:23:34"
|
||||
*/
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
@ApiProperty()
|
||||
updatedAt: Date;
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { EditDto } from '@hedgedoc/commons';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { EditDto } from './edit.dto';
|
||||
import { Edit } from './edit.entity';
|
||||
|
||||
@Injectable()
|
||||
|
@ -15,10 +15,10 @@ export class EditService {
|
|||
|
||||
return {
|
||||
username: authorUser ? authorUser.username : null,
|
||||
startPos: edit.startPos,
|
||||
endPos: edit.endPos,
|
||||
createdAt: edit.createdAt,
|
||||
updatedAt: edit.updatedAt,
|
||||
startPosition: edit.startPos,
|
||||
endPosition: edit.endPos,
|
||||
createdAt: edit.createdAt.toISOString(),
|
||||
updatedAt: edit.updatedAt.toISOString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsArray, IsDate, IsNumber, IsString } from 'class-validator';
|
||||
|
||||
import { BaseDto } from '../utils/base.dto.';
|
||||
import { Revision } from './revision.entity';
|
||||
|
||||
export class RevisionMetadataDto extends BaseDto {
|
||||
/**
|
||||
* ID of this revision
|
||||
* @example 13
|
||||
*/
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
@ApiProperty()
|
||||
id: Revision['id'];
|
||||
|
||||
/**
|
||||
* Datestring of the time this revision was created
|
||||
* @example "2020-12-01 12:23:34"
|
||||
*/
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
@ApiProperty()
|
||||
createdAt: Date;
|
||||
|
||||
/**
|
||||
* Number of characters in this revision
|
||||
* @example 142
|
||||
*/
|
||||
@IsNumber()
|
||||
@ApiProperty()
|
||||
length: number;
|
||||
|
||||
/**
|
||||
* List of the usernames that have contributed to this revision
|
||||
* Does not include anonymous users
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty({ isArray: true, type: String })
|
||||
authorUsernames: string[];
|
||||
|
||||
/**
|
||||
* Count of anonymous users that have contributed to this revision
|
||||
*/
|
||||
@IsNumber()
|
||||
@ApiProperty()
|
||||
anonymousAuthorCount: number;
|
||||
|
||||
/**
|
||||
* Title of the note
|
||||
* Does not contain any markup but might be empty
|
||||
* @example "Shopping List"
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
title: string;
|
||||
|
||||
/**
|
||||
* Description of the note
|
||||
* Does not contain any markup but might be empty
|
||||
* @example Everything I want to buy
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
description: string;
|
||||
|
||||
/**
|
||||
* List of tags assigned to this note
|
||||
* @example "['shopping', 'personal']
|
||||
*/
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
@ApiProperty({ isArray: true, type: String })
|
||||
tags: string[];
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsString, ValidateNested } from 'class-validator';
|
||||
|
||||
import { EditDto } from './edit.dto';
|
||||
import { RevisionMetadataDto } from './revision-metadata.dto';
|
||||
|
||||
export class RevisionDto extends RevisionMetadataDto {
|
||||
/**
|
||||
* Markdown content of the revision
|
||||
* @example "# I am a heading"
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
content: string;
|
||||
|
||||
/**
|
||||
* Patch from the preceding revision to this one
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
patch: string;
|
||||
|
||||
/**
|
||||
* All edit objects which are used in the revision.
|
||||
*/
|
||||
@Type(() => EditDto)
|
||||
@ValidateNested({ each: true })
|
||||
@ApiProperty({ isArray: true, type: EditDto })
|
||||
edits: EditDto[];
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { RevisionDto, RevisionMetadataDto } from '@hedgedoc/commons';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Cron, Timeout } from '@nestjs/schedule';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
@ -15,8 +16,6 @@ import { ConsoleLoggerService } from '../logger/console-logger.service';
|
|||
import { Note } from '../notes/note.entity';
|
||||
import { Tag } from '../notes/tag.entity';
|
||||
import { EditService } from './edit.service';
|
||||
import { RevisionMetadataDto } from './revision-metadata.dto';
|
||||
import { RevisionDto } from './revision.dto';
|
||||
import { Revision } from './revision.entity';
|
||||
import { extractRevisionMetadataFromContent } from './utils/extract-revision-metadata-from-content';
|
||||
|
||||
|
@ -136,7 +135,7 @@ export class RevisionsService {
|
|||
return {
|
||||
id: revision.id,
|
||||
length: revision.length,
|
||||
createdAt: revision.createdAt,
|
||||
createdAt: revision.createdAt.toISOString(),
|
||||
authorUsernames: revisionUserInfo.usernames,
|
||||
anonymousAuthorCount: revisionUserInfo.anonymousUserCount,
|
||||
title: revision.title,
|
||||
|
@ -151,7 +150,7 @@ export class RevisionsService {
|
|||
id: revision.id,
|
||||
content: revision.content,
|
||||
length: revision.length,
|
||||
createdAt: revision.createdAt,
|
||||
createdAt: revision.createdAt.toISOString(),
|
||||
title: revision.title,
|
||||
tags: (await revision.tags).map((tag) => tag.name),
|
||||
description: revision.description,
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ProviderType } from '@hedgedoc/commons';
|
||||
import { DataSource } from 'typeorm';
|
||||
|
||||
import { ApiToken } from './api-token/api-token.entity';
|
||||
import { Identity } from './auth/identity.entity';
|
||||
import { ProviderType } from './auth/provider-type.enum';
|
||||
import { Author } from './authors/author.entity';
|
||||
import { Group } from './groups/group.entity';
|
||||
import { HistoryEntry } from './history/history-entry.entity';
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { FullUserInfoDto, ProviderType } from '@hedgedoc/commons';
|
||||
import { Optional } from '@mrdrogdrog/optional';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
@ -12,16 +13,13 @@ import { unsign } from 'cookie-signature';
|
|||
import { IncomingMessage } from 'http';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { ProviderType } from '../auth/provider-type.enum';
|
||||
import authConfiguration, { AuthConfig } from '../config/auth.config';
|
||||
import { DatabaseType } from '../config/database-type.enum';
|
||||
import databaseConfiguration, {
|
||||
DatabaseConfig,
|
||||
} from '../config/database.config';
|
||||
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
||||
import { FullUserInfoDto } from '../users/user-info.dto';
|
||||
import { HEDGEDOC_SESSION } from '../utils/session';
|
||||
import { Username } from '../utils/username';
|
||||
import { Session } from './session.entity';
|
||||
|
||||
export interface SessionState {
|
||||
|
@ -29,7 +27,7 @@ export interface SessionState {
|
|||
cookie: unknown;
|
||||
|
||||
/** Contains the username if logged in completely, is undefined when not being logged in */
|
||||
username?: Username;
|
||||
username?: string;
|
||||
|
||||
/** The auth provider that is used for the current login or pending login */
|
||||
authProviderType?: ProviderType;
|
||||
|
@ -87,7 +85,7 @@ export class SessionService {
|
|||
* @param sessionId The session id for which the owning user should be found
|
||||
* @return A Promise that either resolves with the username or rejects with an error
|
||||
*/
|
||||
fetchUsernameForSessionId(sessionId: string): Promise<Username | undefined> {
|
||||
fetchUsernameForSessionId(sessionId: string): Promise<string | undefined> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.logger.debug(
|
||||
`Fetching username for sessionId ${sessionId}`,
|
||||
|
@ -102,7 +100,7 @@ export class SessionService {
|
|||
'fetchUsernameForSessionId',
|
||||
);
|
||||
if (error) return reject(error);
|
||||
return resolve(result?.username as Username);
|
||||
return resolve(result?.username);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsLowercase, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
import { BaseDto } from '../utils/base.dto.';
|
||||
import { Username } from '../utils/username';
|
||||
|
||||
export class UserInfoDto extends BaseDto {
|
||||
/**
|
||||
* The username
|
||||
* @example "john.smith"
|
||||
*/
|
||||
@Type(() => String)
|
||||
@IsString()
|
||||
@IsLowercase()
|
||||
@ApiProperty()
|
||||
username: Username;
|
||||
|
||||
/**
|
||||
* The display name
|
||||
* @example "John Smith"
|
||||
*/
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
displayName: string;
|
||||
|
||||
/**
|
||||
* URL of the profile picture
|
||||
* @example "https://hedgedoc.example.com/uploads/johnsmith.png"
|
||||
*/
|
||||
@ApiPropertyOptional({
|
||||
format: 'uri',
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
photoUrl?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* This DTO contains all attributes of the standard UserInfoDto
|
||||
* in addition to the email address.
|
||||
*/
|
||||
export class FullUserInfoDto extends UserInfoDto {
|
||||
/**
|
||||
* Email address of the user
|
||||
* @example "john.smith@example.com"
|
||||
*/
|
||||
@ApiPropertyOptional({
|
||||
format: 'email',
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
email?: string;
|
||||
}
|
||||
|
||||
export class FullUserInfoWithIdDto extends FullUserInfoDto {
|
||||
/**
|
||||
* The user's ID
|
||||
* @example 42
|
||||
*/
|
||||
@IsString()
|
||||
id: string;
|
||||
}
|
||||
|
||||
export class UserLoginInfoDto extends UserInfoDto {
|
||||
/**
|
||||
* Identifier of the auth provider that was used to log in
|
||||
*/
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
authProvider: string;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -20,7 +20,6 @@ import { Group } from '../groups/group.entity';
|
|||
import { HistoryEntry } from '../history/history-entry.entity';
|
||||
import { MediaUpload } from '../media/media-upload.entity';
|
||||
import { Note } from '../notes/note.entity';
|
||||
import { Username } from '../utils/username';
|
||||
|
||||
@Entity()
|
||||
export class User {
|
||||
|
@ -30,7 +29,7 @@ export class User {
|
|||
@Column({
|
||||
unique: true,
|
||||
})
|
||||
username: Username;
|
||||
username: string;
|
||||
|
||||
@Column()
|
||||
displayName: string;
|
||||
|
@ -78,7 +77,7 @@ export class User {
|
|||
private constructor() {}
|
||||
|
||||
public static create(
|
||||
username: Username,
|
||||
username: string,
|
||||
displayName: string,
|
||||
email?: string,
|
||||
photoUrl?: string,
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { IsBoolean, IsLowercase, IsString } from 'class-validator';
|
||||
|
||||
import { BaseDto } from '../utils/base.dto.';
|
||||
import { Username } from '../utils/username';
|
||||
|
||||
export class UsernameCheckDto extends BaseDto {
|
||||
// eslint-disable-next-line @darraghor/nestjs-typed/validated-non-primitive-property-needs-type-decorator
|
||||
@IsString()
|
||||
@IsLowercase()
|
||||
username: Username;
|
||||
}
|
||||
|
||||
export class UsernameCheckResponseDto extends BaseDto {
|
||||
@IsBoolean()
|
||||
usernameAvailable: boolean;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -54,7 +54,7 @@ describe('UsersService', () => {
|
|||
.mockImplementationOnce(async (user: User): Promise<User> => user);
|
||||
});
|
||||
it('successfully creates a user', async () => {
|
||||
const user = await service.createUser(username, displayname);
|
||||
const user = await service.createUser(username, displayname, null, null);
|
||||
expect(user.username).toEqual(username);
|
||||
expect(user.displayName).toEqual(displayname);
|
||||
});
|
||||
|
@ -64,11 +64,11 @@ describe('UsersService', () => {
|
|||
throw new Error();
|
||||
});
|
||||
// create first user with username
|
||||
await service.createUser(username, displayname);
|
||||
await service.createUser(username, displayname, null, null);
|
||||
// attempt to create second user with username
|
||||
await expect(service.createUser(username, displayname)).rejects.toThrow(
|
||||
AlreadyInDBError,
|
||||
);
|
||||
await expect(
|
||||
service.createUser(username, displayname, null, null),
|
||||
).rejects.toThrow(AlreadyInDBError);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -134,7 +134,7 @@ describe('UsersService', () => {
|
|||
expect(photoUrl).toEqual(photo);
|
||||
});
|
||||
it('works if a user no photoUrl', () => {
|
||||
user.photo = undefined;
|
||||
user.photo = null;
|
||||
const photoUrl = service.getPhotoUrl(user);
|
||||
expect(photoUrl).toEqual('');
|
||||
});
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { REGEX_USERNAME } from '@hedgedoc/commons';
|
||||
import {
|
||||
FullUserInfoDto,
|
||||
LoginUserInfoDto,
|
||||
ProviderType,
|
||||
REGEX_USERNAME,
|
||||
UserInfoDto,
|
||||
} from '@hedgedoc/commons';
|
||||
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
|
@ -11,12 +17,6 @@ import { Repository } from 'typeorm';
|
|||
import AuthConfiguration, { AuthConfig } from '../config/auth.config';
|
||||
import { AlreadyInDBError, NotInDBError } from '../errors/errors';
|
||||
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
||||
import { Username } from '../utils/username';
|
||||
import {
|
||||
FullUserInfoDto,
|
||||
UserInfoDto,
|
||||
UserLoginInfoDto,
|
||||
} from './user-info.dto';
|
||||
import { UserRelationEnum } from './user-relation.enum';
|
||||
import { User } from './user.entity';
|
||||
|
||||
|
@ -34,7 +34,7 @@ export class UsersService {
|
|||
/**
|
||||
* @async
|
||||
* Create a new user with a given username and displayName
|
||||
* @param {Username} username - the username the new user shall have
|
||||
* @param {string} username - the username the new user shall have
|
||||
* @param {string} displayName - the display name the new user shall have
|
||||
* @param {string} [email] - the email the new user shall have
|
||||
* @param {string} [photoUrl] - the photoUrl the new user shall have
|
||||
|
@ -43,17 +43,22 @@ export class UsersService {
|
|||
* @throws {AlreadyInDBError} the username is already taken.
|
||||
*/
|
||||
async createUser(
|
||||
username: Username,
|
||||
username: string,
|
||||
displayName: string,
|
||||
email?: string,
|
||||
photoUrl?: string,
|
||||
email: string | null,
|
||||
photoUrl: string | null,
|
||||
): Promise<User> {
|
||||
if (!REGEX_USERNAME.test(username)) {
|
||||
throw new BadRequestException(
|
||||
`The username '${username}' is not a valid username.`,
|
||||
);
|
||||
}
|
||||
const user = User.create(username, displayName, email, photoUrl);
|
||||
const user = User.create(
|
||||
username,
|
||||
displayName,
|
||||
email || undefined,
|
||||
photoUrl || undefined,
|
||||
);
|
||||
try {
|
||||
return await this.userRepository.save(user);
|
||||
} catch {
|
||||
|
@ -123,7 +128,7 @@ export class UsersService {
|
|||
* @param username - the username to check
|
||||
* @return {boolean} true if the user exists, false otherwise
|
||||
*/
|
||||
async checkIfUserExists(username: Username): Promise<boolean> {
|
||||
async checkIfUserExists(username: string): Promise<boolean> {
|
||||
const user = await this.userRepository.findOne({
|
||||
where: { username: username },
|
||||
});
|
||||
|
@ -133,12 +138,12 @@ export class UsersService {
|
|||
/**
|
||||
* @async
|
||||
* Get the user specified by the username
|
||||
* @param {Username} username the username by which the user is specified
|
||||
* @param {string} username the username by which the user is specified
|
||||
* @param {UserRelationEnum[]} [withRelations=[]] if the returned user object should contain certain relations
|
||||
* @return {User} the specified user
|
||||
*/
|
||||
async getUserByUsername(
|
||||
username: Username,
|
||||
username: string,
|
||||
withRelations: UserRelationEnum[] = [],
|
||||
): Promise<User> {
|
||||
const user = await this.userRepository.findOne({
|
||||
|
@ -191,7 +196,7 @@ export class UsersService {
|
|||
};
|
||||
}
|
||||
|
||||
toUserLoginInfoDto(user: User, authProvider: string): UserLoginInfoDto {
|
||||
return { ...this.toUserDto(user), authProvider };
|
||||
toLoginUserInfoDto(user: User, authProvider: ProviderType): LoginUserInfoDto {
|
||||
return { ...this.toFullUserDto(user), authProvider };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +1,29 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ServerVersionDto } from '@hedgedoc/commons';
|
||||
import { Optional } from '@mrdrogdrog/optional';
|
||||
import { promises as fs } from 'fs';
|
||||
import { join as joinPath } from 'path';
|
||||
|
||||
import { ServerVersion } from '../monitoring/server-status.dto';
|
||||
|
||||
let versionCache: ServerVersion | undefined = undefined;
|
||||
let versionCache: ServerVersionDto | undefined = undefined;
|
||||
|
||||
/**
|
||||
* Reads the HedgeDoc version from the root package.json. This is done only once per run.
|
||||
*
|
||||
* @return {Promise<ServerVersion>} A Promise that contains the parsed server version.
|
||||
* @return {Promise<ServerVersionDto>} A Promise that contains the parsed server version.
|
||||
* @throws {Error} if the package.json couldn't be found or doesn't contain a correct version.
|
||||
*/
|
||||
export async function getServerVersionFromPackageJson(): Promise<ServerVersion> {
|
||||
export async function getServerVersionFromPackageJson(): Promise<ServerVersionDto> {
|
||||
if (!versionCache) {
|
||||
versionCache = await parseVersionFromPackageJson();
|
||||
}
|
||||
return versionCache;
|
||||
}
|
||||
|
||||
async function parseVersionFromPackageJson(): Promise<ServerVersion> {
|
||||
async function parseVersionFromPackageJson(): Promise<ServerVersionDto> {
|
||||
const rawFileContent: string = await fs.readFile(
|
||||
joinPath(__dirname, '../../../package.json'),
|
||||
{ encoding: 'utf8' },
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export type TimestampMillis = number;
|
|
@ -1,11 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export type Username = Lowercase<string>;
|
||||
|
||||
export function makeUsernameLowercase(username: string): Username {
|
||||
return username.toLowerCase() as Username;
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { AliasCreateDto, AliasUpdateDto } from '@hedgedoc/commons';
|
||||
import request from 'supertest';
|
||||
|
||||
import { AliasCreateDto } from '../../src/notes/alias-create.dto';
|
||||
import { AliasUpdateDto } from '../../src/notes/alias-update.dto';
|
||||
import { Note } from '../../src/notes/note.entity';
|
||||
import { User } from '../../src/users/user.entity';
|
||||
import {
|
||||
password1,
|
||||
|
@ -183,11 +183,12 @@ describe('Alias', () => {
|
|||
});
|
||||
});
|
||||
it('if the property primaryAlias is false', async () => {
|
||||
changeAliasDto.primaryAlias = false;
|
||||
await agent1
|
||||
.put(`/api/private/alias/${newAlias}`)
|
||||
.set('Content-Type', 'application/json')
|
||||
.send(changeAliasDto)
|
||||
.send({
|
||||
primaryAlias: false,
|
||||
})
|
||||
.expect(400);
|
||||
});
|
||||
it('if the user is not an owner', async () => {
|
||||
|
@ -204,7 +205,7 @@ describe('Alias', () => {
|
|||
describe('DELETE /alias/{alias}', () => {
|
||||
const testAlias = 'aliasTest3';
|
||||
const newAlias = 'normalAlias3';
|
||||
let note;
|
||||
let note: Note;
|
||||
|
||||
beforeEach(async () => {
|
||||
note = await testSetup.notesService.createNote(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -8,21 +8,18 @@
|
|||
@typescript-eslint/no-unsafe-assignment,
|
||||
@typescript-eslint/no-unsafe-member-access
|
||||
*/
|
||||
import { LoginDto, RegisterDto, UpdatePasswordDto } from '@hedgedoc/commons';
|
||||
import request from 'supertest';
|
||||
|
||||
import { LoginDto } from '../../src/auth/local/login.dto';
|
||||
import { RegisterDto } from '../../src/auth/local/register.dto';
|
||||
import { UpdatePasswordDto } from '../../src/auth/local/update-password.dto';
|
||||
import { NotInDBError } from '../../src/errors/errors';
|
||||
import { UserRelationEnum } from '../../src/users/user-relation.enum';
|
||||
import { checkPassword } from '../../src/utils/password';
|
||||
import { Username } from '../../src/utils/username';
|
||||
import { TestSetup, TestSetupBuilder } from '../test-setup';
|
||||
|
||||
describe('Auth', () => {
|
||||
let testSetup: TestSetup;
|
||||
|
||||
let username: Username;
|
||||
let username: string;
|
||||
let displayName: string;
|
||||
let password: string;
|
||||
|
||||
|
@ -75,6 +72,8 @@ describe('Auth', () => {
|
|||
const conflictingUser = await testSetup.userService.createUser(
|
||||
conflictingUserName,
|
||||
displayName,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
const registrationDto: RegisterDto = {
|
||||
displayName: displayName,
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { GuestAccess, LoginDto } from '@hedgedoc/commons';
|
||||
import request from 'supertest';
|
||||
|
||||
import { LoginDto } from '../../src/auth/local/login.dto';
|
||||
import { GuestAccess } from '../../src/config/guest_access.enum';
|
||||
import { createDefaultMockNoteConfig } from '../../src/config/mock/note.config.mock';
|
||||
import { NoteConfig } from '../../src/config/note.config';
|
||||
import {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -41,7 +41,7 @@ describe('History', () => {
|
|||
historyService = moduleRef.get(HistoryService);
|
||||
const userService = moduleRef.get(UsersService);
|
||||
localIdentityService = moduleRef.get(LocalService);
|
||||
user = await userService.createUser(username, 'Testy');
|
||||
user = await userService.createUser(username, 'Testy', null, null);
|
||||
await localIdentityService.createLocalIdentity(user, password);
|
||||
const notesService = moduleRef.get(NotesService);
|
||||
note = await notesService.createNote(content, user, 'note');
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { LoginUserInfoDto, ProviderType } from '@hedgedoc/commons';
|
||||
import { promises as fs } from 'fs';
|
||||
import request from 'supertest';
|
||||
|
||||
import { NotInDBError } from '../../src/errors/errors';
|
||||
import { Note } from '../../src/notes/note.entity';
|
||||
import { UserLoginInfoDto } from '../../src/users/user-info.dto';
|
||||
import { User } from '../../src/users/user.entity';
|
||||
import { TestSetup, TestSetupBuilder } from '../test-setup';
|
||||
|
||||
|
@ -32,7 +32,12 @@ describe('Me', () => {
|
|||
const password = 'AHardcodedStrongP@ssword123';
|
||||
await testSetup.app.init();
|
||||
|
||||
user = await testSetup.userService.createUser(username, 'Testy');
|
||||
user = await testSetup.userService.createUser(
|
||||
username,
|
||||
'Testy',
|
||||
null,
|
||||
null,
|
||||
);
|
||||
await testSetup.localIdentityService.createLocalIdentity(user, password);
|
||||
|
||||
content = 'This is a test note.';
|
||||
|
@ -51,12 +56,15 @@ describe('Me', () => {
|
|||
});
|
||||
|
||||
it('GET /me', async () => {
|
||||
const userInfo = testSetup.userService.toUserLoginInfoDto(user, 'local');
|
||||
const userInfo = testSetup.userService.toLoginUserInfoDto(
|
||||
user,
|
||||
ProviderType.LOCAL,
|
||||
);
|
||||
const response = await agent
|
||||
.get('/api/private/me')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200);
|
||||
const gotUser = response.body as UserLoginInfoDto;
|
||||
const gotUser = response.body as LoginUserInfoDto;
|
||||
expect(gotUser).toEqual(userInfo);
|
||||
});
|
||||
|
||||
|
@ -126,15 +134,15 @@ describe('Me', () => {
|
|||
await fs.rmdir(uploadPath);
|
||||
});
|
||||
|
||||
it('POST /me/profile', async () => {
|
||||
it('PUT /me/profile', async () => {
|
||||
const newDisplayName = 'Another name';
|
||||
expect(user.displayName).not.toEqual(newDisplayName);
|
||||
await agent
|
||||
.post('/api/private/me/profile')
|
||||
.put('/api/private/me/profile')
|
||||
.send({
|
||||
displayName: newDisplayName,
|
||||
})
|
||||
.expect(201);
|
||||
.expect(200);
|
||||
const dbUser = await testSetup.userService.getUserByUsername('hardcoded');
|
||||
expect(dbUser.displayName).toEqual(newDisplayName);
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -39,9 +39,19 @@ describe('Notes', () => {
|
|||
const password2 = 'AHardcodedStrongP@ssword12';
|
||||
const groupname1 = 'groupname1';
|
||||
|
||||
user1 = await testSetup.userService.createUser(username1, 'Testy');
|
||||
user1 = await testSetup.userService.createUser(
|
||||
username1,
|
||||
'Testy',
|
||||
null,
|
||||
null,
|
||||
);
|
||||
await testSetup.localIdentityService.createLocalIdentity(user1, password1);
|
||||
user2 = await testSetup.userService.createUser(username2, 'Max Mustermann');
|
||||
user2 = await testSetup.userService.createUser(
|
||||
username2,
|
||||
'Max Mustermann',
|
||||
null,
|
||||
null,
|
||||
);
|
||||
await testSetup.localIdentityService.createLocalIdentity(user2, password2);
|
||||
|
||||
group1 = await testSetup.groupService.createGroup(groupname1, 'Group 1');
|
||||
|
@ -668,7 +678,7 @@ describe('Notes', () => {
|
|||
.put(`/api/private/notes/${alias}/metadata/permissions/owner`)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200)
|
||||
.send({ newOwner: user2.username });
|
||||
.send({ owner: user2.username });
|
||||
expect(response.body.metadata.permissions.owner).toBe(user2.username);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { LoginDto, RegisterDto } from '@hedgedoc/commons';
|
||||
import request from 'supertest';
|
||||
|
||||
import { LoginDto } from '../../src/auth/local/login.dto';
|
||||
import { RegisterDto } from '../../src/auth/local/register.dto';
|
||||
import { TestSetup, TestSetupBuilder } from '../test-setup';
|
||||
|
||||
describe('Register and Login', () => {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { AliasUpdateDto } from '@hedgedoc/commons';
|
||||
import request from 'supertest';
|
||||
|
||||
import { AliasUpdateDto } from '../../src/notes/alias-update.dto';
|
||||
import { TestSetup, TestSetupBuilder } from '../test-setup';
|
||||
|
||||
describe('Alias', () => {
|
||||
|
@ -166,13 +166,13 @@ describe('Alias', () => {
|
|||
.expect(401);
|
||||
});
|
||||
it('if the property primaryAlias is false', async () => {
|
||||
changeAliasDto.primaryAlias = false;
|
||||
|
||||
await request(testSetup.app.getHttpServer())
|
||||
.put(`/api/v2/alias/${testAlias}`)
|
||||
.set('Authorization', `Bearer ${testSetup.authTokens[0].secret}`)
|
||||
.set('Content-Type', 'application/json')
|
||||
.send(changeAliasDto)
|
||||
.send({
|
||||
primaryAlias: false,
|
||||
})
|
||||
.expect(400);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { NoteMetadataDto } from '@hedgedoc/commons';
|
||||
import { promises as fs } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { HistoryEntryDto } from 'src/history/history-entry.dto';
|
||||
import request from 'supertest';
|
||||
|
||||
import { HistoryEntryUpdateDto } from '../../src/history/history-entry-update.dto';
|
||||
import { HistoryEntryDto } from '../../src/history/history-entry.dto';
|
||||
import { NoteMetadataDto } from '../../src/notes/note-metadata.dto';
|
||||
import { User } from '../../src/users/user.entity';
|
||||
import { TestSetup, TestSetupBuilder } from '../test-setup';
|
||||
|
||||
|
@ -25,7 +25,12 @@ describe('Me', () => {
|
|||
uploadPath =
|
||||
testSetup.configService.get('mediaConfig').backend.filesystem.uploadPath;
|
||||
|
||||
user = await testSetup.userService.createUser('hardcoded', 'Testy');
|
||||
user = await testSetup.userService.createUser(
|
||||
'hardcoded',
|
||||
'Testy',
|
||||
null,
|
||||
null,
|
||||
);
|
||||
await testSetup.app.init();
|
||||
});
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { NotePermissionsUpdateDto } from '@hedgedoc/commons';
|
||||
import { promises as fs } from 'fs';
|
||||
import { join } from 'path';
|
||||
import request from 'supertest';
|
||||
|
||||
import { NotInDBError } from '../../src/errors/errors';
|
||||
import { NotePermissionsUpdateDto } from '../../src/notes/note-permissions.dto';
|
||||
import { TestSetup, TestSetupBuilder } from '../test-setup';
|
||||
|
||||
describe('Notes', () => {
|
||||
|
@ -219,14 +219,15 @@ describe('Notes', () => {
|
|||
testSetup.users[0],
|
||||
'deleteTest3',
|
||||
);
|
||||
const updateNotePermission = new NotePermissionsUpdateDto();
|
||||
updateNotePermission.sharedToUsers = [
|
||||
{
|
||||
username: testSetup.users[0].username,
|
||||
canEdit: true,
|
||||
},
|
||||
];
|
||||
updateNotePermission.sharedToGroups = [];
|
||||
const updateNotePermission: NotePermissionsUpdateDto = {
|
||||
sharedToUsers: [
|
||||
{
|
||||
username: testSetup.users[0].username,
|
||||
canEdit: true,
|
||||
},
|
||||
],
|
||||
sharedToGroups: [],
|
||||
};
|
||||
await testSetup.permissionsService.updateNotePermissions(
|
||||
note,
|
||||
updateNotePermission,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ApiTokenWithSecretDto } from '@hedgedoc/commons';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { RouterModule, Routes } from '@nestjs/core';
|
||||
import { EventEmitterModule } from '@nestjs/event-emitter';
|
||||
|
@ -11,7 +12,6 @@ import { Test, TestingModule, TestingModuleBuilder } from '@nestjs/testing';
|
|||
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
|
||||
import { Connection, createConnection } from 'typeorm';
|
||||
|
||||
import { ApiTokenWithSecretDto } from '../src/api-token/api-token.dto';
|
||||
import { ApiTokenGuard } from '../src/api-token/api-token.guard';
|
||||
import { ApiTokenModule } from '../src/api-token/api-token.module';
|
||||
import { ApiTokenService } from '../src/api-token/api-token.service';
|
||||
|
@ -389,13 +389,28 @@ export class TestSetupBuilder {
|
|||
this.setupPostCompile.push(async () => {
|
||||
// Create users
|
||||
this.testSetup.users.push(
|
||||
await this.testSetup.userService.createUser(username1, 'Test User 1'),
|
||||
await this.testSetup.userService.createUser(
|
||||
username1,
|
||||
'Test User 1',
|
||||
null,
|
||||
null,
|
||||
),
|
||||
);
|
||||
this.testSetup.users.push(
|
||||
await this.testSetup.userService.createUser(username2, 'Test User 2'),
|
||||
await this.testSetup.userService.createUser(
|
||||
username2,
|
||||
'Test User 2',
|
||||
null,
|
||||
null,
|
||||
),
|
||||
);
|
||||
this.testSetup.users.push(
|
||||
await this.testSetup.userService.createUser(username3, 'Test User 3'),
|
||||
await this.testSetup.userService.createUser(
|
||||
username3,
|
||||
'Test User 3',
|
||||
null,
|
||||
null,
|
||||
),
|
||||
);
|
||||
|
||||
// Create identities for login
|
||||
|
@ -415,10 +430,12 @@ export class TestSetupBuilder {
|
|||
// create auth tokens
|
||||
this.testSetup.authTokens = await Promise.all(
|
||||
this.testSetup.users.map(async (user) => {
|
||||
const validUntil = new Date();
|
||||
validUntil.setTime(validUntil.getTime() + 60 * 60 * 1000);
|
||||
return await this.testSetup.publicAuthTokenService.addToken(
|
||||
user,
|
||||
'test',
|
||||
new Date().getTime() + 60 * 60 * 1000,
|
||||
validUntil,
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue