mirror of
https://github.com/CaramelFur/Picsur.git
synced 2025-11-14 07:45:39 +01:00
make sure typeorm stays in backend
This commit is contained in:
@@ -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,
|
||||
|
||||
23
backend/src/backenddto/image.entity.ts
Normal file
23
backend/src/backenddto/image.entity.ts
Normal 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;
|
||||
}
|
||||
20
backend/src/backenddto/user.entity.ts
Normal file
20
backend/src/backenddto/user.entity.ts
Normal 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;
|
||||
}
|
||||
@@ -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],
|
||||
})
|
||||
|
||||
@@ -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 } },
|
||||
|
||||
@@ -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],
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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, {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user