make sure typeorm stays in backend

This commit is contained in:
rubikscraft
2022-03-01 22:05:59 +01:00
parent 05c0ad0e28
commit 0e0060ffb5
16 changed files with 108 additions and 67 deletions

View File

@@ -5,8 +5,8 @@ import { ImageModule } from './routes/image/imageroute.module';
import { ServeStaticModule } from '@nestjs/serve-static';
import Config from './env';
import { DemoManagerModule } from './managers/demo/demomanager.module';
import { EUser } from 'picsur-shared/dist/entities/user.entity';
import { EImage } from 'picsur-shared/dist/entities/image.entity';
import { EImageBackend } from './backenddto/image.entity';
import { EUserBackend } from './backenddto/user.entity';
@Module({
imports: [
@@ -19,7 +19,7 @@ import { EImage } from 'picsur-shared/dist/entities/image.entity';
database: Config.database.database,
synchronize: true,
entities: [EUser, EImage],
entities: [EUserBackend, EImageBackend],
}),
ServeStaticModule.forRoot({
rootPath: Config.static.frontendRoot,

View File

@@ -0,0 +1,23 @@
import {
SupportedMime,
SupportedMimes,
} from 'picsur-shared/dist/dto/mimes.dto';
import { EImage } from 'picsur-shared/dist/entities/image.entity';
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class EImageBackend extends EImage {
@PrimaryGeneratedColumn()
override id?: number;
@Index()
@Column({ unique: true })
override hash: string;
// Binary data
@Column({ type: 'bytea', nullable: false, select: false })
override data?: Buffer;
@Column({ enum: SupportedMimes })
override mime: SupportedMime;
}

View File

@@ -0,0 +1,20 @@
import { EUser } from 'picsur-shared/dist/entities/user.entity';
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
// Different data for public and private
@Entity()
export class EUserBackend extends EUser {
@PrimaryGeneratedColumn()
override id?: number;
@Index()
@Column({ unique: true })
override username: string;
@Column({ default: false })
override isAdmin: boolean;
@Column({ select: false })
override password?: string;
}

View File

@@ -1,11 +1,11 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { EImageBackend } from '../../backenddto/image.entity';
import { ImageDBService } from './imagedb.service';
import { MimesService } from './mimes.service';
import { EImage } from 'picsur-shared/dist/entities/image.entity';
@Module({
imports: [TypeOrmModule.forFeature([EImage])],
imports: [TypeOrmModule.forFeature([EImageBackend])],
providers: [ImageDBService, MimesService],
exports: [ImageDBService, MimesService],
})

View File

@@ -10,25 +10,25 @@ import {
} from 'picsur-shared/dist/types';
import { SupportedMime } from 'picsur-shared/dist/dto/mimes.dto';
import { GetCols } from '../collectionutils';
import { EImage } from 'picsur-shared/dist/entities/image.entity';
import { plainToClass } from 'class-transformer';
import { EImageBackend } from '../../backenddto/image.entity';
@Injectable()
export class ImageDBService {
constructor(
@InjectRepository(EImage)
private imageRepository: Repository<EImage>,
@InjectRepository(EImageBackend)
private imageRepository: Repository<EImageBackend>,
) {}
public async create(
image: Buffer,
type: SupportedMime,
): AsyncFailable<EImage> {
): AsyncFailable<EImageBackend> {
const hash = this.hash(image);
const find = await this.findOne(hash);
if (HasSuccess(find)) return find;
let imageEntity = new EImage();
let imageEntity = new EImageBackend();
imageEntity.data = image;
imageEntity.mime = type;
imageEntity.hash = hash;
@@ -40,13 +40,13 @@ export class ImageDBService {
}
// Strips unwanted data
return plainToClass(EImage, imageEntity);
return plainToClass(EImageBackend, imageEntity);
}
public async findOne<B extends true | undefined = undefined>(
hash: string,
getPrivate?: B,
): AsyncFailable<B extends undefined ? EImage : Required<EImage>> {
): AsyncFailable<B extends undefined ? EImageBackend : Required<EImageBackend>> {
try {
const found = await this.imageRepository.findOne({
where: { hash },
@@ -54,7 +54,7 @@ export class ImageDBService {
});
if (!found) return Fail('Image not found');
return found as B extends undefined ? EImage : Required<EImage>;
return found as B extends undefined ? EImageBackend : Required<EImageBackend>;
} catch (e: any) {
return Fail(e?.message);
}
@@ -63,7 +63,7 @@ export class ImageDBService {
public async findMany(
startId: number,
limit: number,
): AsyncFailable<EImage[]> {
): AsyncFailable<EImageBackend[]> {
try {
const found = await this.imageRepository.find({
where: { id: { gte: startId } },

View File

@@ -1,10 +1,10 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { EUser } from 'picsur-shared/dist/entities/user.entity';
import { EUserBackend } from '../../backenddto/user.entity';
import { UsersService } from './userdb.service';
@Module({
imports: [TypeOrmModule.forFeature([EUser])],
imports: [TypeOrmModule.forFeature([EUserBackend])],
providers: [UsersService],
exports: [UsersService],
})

View File

@@ -2,7 +2,6 @@ import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { plainToClass } from 'class-transformer';
import { validate } from 'class-validator';
import { EUser } from 'picsur-shared/dist/entities/user.entity';
import {
AsyncFailable,
Fail,
@@ -10,6 +9,7 @@ import {
HasSuccess,
} from 'picsur-shared/dist/types';
import { Repository } from 'typeorm';
import { EUserBackend } from '../../backenddto/user.entity';
import { GetCols } from '../collectionutils';
@Injectable()
@@ -17,17 +17,17 @@ export class UsersService {
private readonly logger = new Logger('UsersService');
constructor(
@InjectRepository(EUser)
private usersRepository: Repository<EUser>,
@InjectRepository(EUserBackend)
private usersRepository: Repository<EUserBackend>,
) {}
public async create(
username: string,
hashedPassword: string,
): AsyncFailable<EUser> {
): AsyncFailable<EUserBackend> {
if (await this.exists(username)) return Fail('User already exists');
let user = new EUser();
let user = new EUserBackend();
user.username = username;
user.password = hashedPassword;
@@ -37,11 +37,11 @@ export class UsersService {
return Fail(e?.message);
}
return plainToClass(EUser, user); // Strips unwanted data
return plainToClass(EUserBackend, user); // Strips unwanted data
}
// Returns user object without id
public async delete(user: string | EUser): AsyncFailable<EUser> {
public async delete(user: string | EUserBackend): AsyncFailable<EUserBackend> {
const userToModify = await this.resolve(user);
if (HasFailed(userToModify)) return userToModify;
@@ -55,7 +55,7 @@ export class UsersService {
public async findOne<B extends true | undefined = undefined>(
username: string,
getPrivate?: B,
): AsyncFailable<B extends undefined ? EUser : Required<EUser>> {
): AsyncFailable<B extends undefined ? EUserBackend : Required<EUserBackend>> {
try {
const found = await this.usersRepository.findOne({
where: { username },
@@ -63,13 +63,13 @@ export class UsersService {
});
if (!found) return Fail('User not found');
return found as B extends undefined ? EUser : Required<EUser>;
return found as B extends undefined ? EUserBackend : Required<EUserBackend>;
} catch (e: any) {
return Fail(e?.message);
}
}
public async findAll(): AsyncFailable<EUser[]> {
public async findAll(): AsyncFailable<EUserBackend[]> {
try {
return await this.usersRepository.find();
} catch (e: any) {
@@ -82,7 +82,7 @@ export class UsersService {
}
public async modifyAdmin(
user: string | EUser,
user: string | EUserBackend,
admin: boolean,
): AsyncFailable<true> {
const userToModify = await this.resolve(user);
@@ -94,11 +94,11 @@ export class UsersService {
return true;
}
private async resolve(user: string | EUser): AsyncFailable<EUser> {
private async resolve(user: string | EUserBackend): AsyncFailable<EUserBackend> {
if (typeof user === 'string') {
return await this.findOne(user);
} else {
user = plainToClass(EUser, user);
user = plainToClass(EUserBackend, user);
const errors = await validate(user, { forbidUnknownValues: true });
if (errors.length > 0) {
this.logger.warn(errors);

View File

@@ -1,9 +1,8 @@
import { Injectable } from '@nestjs/common';
import { isHash } from 'class-validator';
import { fileTypeFromBuffer, FileTypeResult } from 'file-type';
import { FullMime } from 'picsur-shared/dist/dto/mimes.dto';
import { EImage } from 'picsur-shared/dist/entities/image.entity';
import { AsyncFailable, Fail, HasFailed } from 'picsur-shared/dist/types';
import { AsyncFailable, HasFailed } from 'picsur-shared/dist/types';
import { EImageBackend } from '../../backenddto/image.entity';
import { ImageDBService } from '../../collections/imagedb/imagedb.service';
import { MimesService } from '../../collections/imagedb/mimes.service';
@@ -14,16 +13,16 @@ export class ImageManagerService {
private readonly mimesService: MimesService,
) {}
public async retrieveInfo(hash: string): AsyncFailable<EImage> {
public async retrieveInfo(hash: string): AsyncFailable<EImageBackend> {
return await this.imagesService.findOne(hash);
}
// Image data buffer is not included by default, this also returns that buffer
public async retrieveComplete(hash: string): AsyncFailable<Required<EImage>> {
public async retrieveComplete(hash: string): AsyncFailable<Required<EImageBackend>> {
return await this.imagesService.findOne(hash, true);
}
public async upload(image: Buffer): AsyncFailable<EImage> {
public async upload(image: Buffer): AsyncFailable<EImageBackend> {
const fullMime = await this.getFullMimeFromBuffer(image);
if (HasFailed(fullMime)) return fullMime;

View File

@@ -6,7 +6,7 @@ import {
} from '@nestjs/common';
import { plainToClass } from 'class-transformer';
import { validate } from 'class-validator';
import { EUser } from 'picsur-shared/dist/entities/user.entity';
import { EUserBackend } from '../../../backenddto/user.entity';
@Injectable()
export class AdminGuard implements CanActivate {
@@ -15,7 +15,7 @@ export class AdminGuard implements CanActivate {
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const user = plainToClass(EUser, request.user);
const user = plainToClass(EUserBackend, request.user);
const errors = await validate(user, {forbidUnknownValues: true});
if (errors.length > 0) {
this.logger.warn(errors);

View File

@@ -4,8 +4,8 @@ import * as bcrypt from 'bcrypt';
import { instanceToPlain, plainToClass } from 'class-transformer';
import { validate } from 'class-validator';
import { JwtDataDto } from 'picsur-shared/dist/dto/auth.dto';
import { EUser } from 'picsur-shared/dist/entities/user.entity';
import { AsyncFailable, HasFailed, Fail } from 'picsur-shared/dist/types';
import { EUserBackend } from '../../../backenddto/user.entity';
import { UsersService } from '../../../collections/userdb/userdb.service';
@Injectable()
@@ -17,20 +17,20 @@ export class AuthService {
private jwtService: JwtService,
) {}
async createUser(username: string, password: string): AsyncFailable<EUser> {
async createUser(username: string, password: string): AsyncFailable<EUserBackend> {
const hashedPassword = await bcrypt.hash(password, 12);
return this.usersService.create(username, hashedPassword);
}
async deleteUser(user: string | EUser): AsyncFailable<EUser> {
async deleteUser(user: string | EUserBackend): AsyncFailable<EUserBackend> {
return this.usersService.delete(user);
}
async listUsers(): AsyncFailable<EUser[]> {
async listUsers(): AsyncFailable<EUserBackend[]> {
return this.usersService.findAll();
}
async authenticate(username: string, password: string): AsyncFailable<EUser> {
async authenticate(username: string, password: string): AsyncFailable<EUserBackend> {
const user = await this.usersService.findOne(username, true);
if (HasFailed(user)) return user;
@@ -40,7 +40,7 @@ export class AuthService {
return await this.usersService.findOne(username);
}
async createToken(user: EUser): Promise<string> {
async createToken(user: EUserBackend): Promise<string> {
const jwtData: JwtDataDto = plainToClass(JwtDataDto, {
user,
});
@@ -54,11 +54,11 @@ export class AuthService {
return this.jwtService.signAsync(instanceToPlain(jwtData));
}
async makeAdmin(user: string | EUser): AsyncFailable<true> {
async makeAdmin(user: string | EUserBackend): AsyncFailable<true> {
return this.usersService.modifyAdmin(user, true);
}
async revokeAdmin(user: string | EUser): AsyncFailable<true> {
async revokeAdmin(user: string | EUserBackend): AsyncFailable<true> {
return this.usersService.modifyAdmin(user, false);
}
}

View File

@@ -1,6 +1,6 @@
import { FastifyRequest } from 'fastify';
import { EUser } from 'picsur-shared/dist/entities/user.entity';
import { EUserBackend } from '../../../backenddto/user.entity';
export default interface AuthFasityRequest extends FastifyRequest {
user: EUser;
user: EUserBackend;
}

View File

@@ -5,7 +5,7 @@ import { validate } from 'class-validator';
import { plainToClass } from 'class-transformer';
import Config from '../../../env';
import { JwtDataDto } from 'picsur-shared/dist/dto/auth.dto';
import { EUser } from 'picsur-shared/dist/entities/user.entity';
import { EUserBackend } from '../../../backenddto/user.entity';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
@@ -19,7 +19,7 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
});
}
async validate(payload: any): Promise<EUser> {
async validate(payload: any): Promise<EUserBackend> {
const jwt = plainToClass(JwtDataDto, payload);
const errors = await validate(jwt, {

View File

@@ -3,7 +3,7 @@ import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AsyncFailable, HasFailed } from 'picsur-shared/dist/types';
import { EUser } from 'picsur-shared/dist/entities/user.entity';
import { EUserBackend } from '../../../backenddto/user.entity';
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy, 'local') {
@@ -11,7 +11,7 @@ export class LocalStrategy extends PassportStrategy(Strategy, 'local') {
super();
}
async validate(username: string, password: string): AsyncFailable<EUser> {
async validate(username: string, password: string): AsyncFailable<EUserBackend> {
const user = await this.authService.authenticate(username, password);
if (HasFailed(user)) {
throw new UnauthorizedException();

View File

@@ -11,8 +11,7 @@
"dependencies": {
"class-transformer": "^0.5.1",
"class-validator": "^0.13.2",
"tsc-watch": "^4.6.0",
"typeorm": "^0.2.44"
"tsc-watch": "^4.6.0"
},
"devDependencies": {
"@types/node": "^17.0.21",

View File

@@ -5,27 +5,27 @@ import {
IsHash,
IsOptional,
} from 'class-validator';
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
//import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
import { SupportedMime, SupportedMimes } from '../dto/mimes.dto';
@Entity()
//@Entity()
export class EImage {
@PrimaryGeneratedColumn()
// @PrimaryGeneratedColumn()
@IsOptional()
id?: number;
@Index()
@Column({ unique: true })
// @Index()
// @Column({ unique: true })
@IsHash('sha256')
hash: string;
// Binary data
@Column({ type: 'bytea', nullable: false, select: false })
// @Column({ type: 'bytea', nullable: false, select: false })
@IsOptional()
@Exclude()
data?: Buffer;
@Column({ enum: SupportedMimes })
// @Column({ enum: SupportedMimes })
@IsEnum(SupportedMimes)
@IsDefined()
mime: SupportedMime;

View File

@@ -5,26 +5,26 @@ import {
IsNotEmpty,
IsOptional,
} from 'class-validator';
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
// import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
// Different data for public and private
@Entity()
// @Entity()
export class EUser {
@PrimaryGeneratedColumn()
// @PrimaryGeneratedColumn()
@IsOptional()
id?: number;
@Index()
@Column({ unique: true })
// @Index()
// @Column({ unique: true })
@IsNotEmpty()
username: string;
@Column({ default: false })
// @Column({ default: false })
@IsDefined()
isAdmin: boolean;
@Column({ select: false })
// @Column({ select: false })
@IsOptional()
@Exclude()
password?: string;