From eb3ce6e9cf8c77dc63ed7b1e3f983711fe858c45 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 14 Feb 2025 13:21:38 +0100 Subject: [PATCH] add graphql handling and adjust entity --- backend/src/auth/ADMIN_RIGHTS.ts | 1 + backend/src/auth/INALIENABLE_RIGHTS.ts | 1 + backend/src/auth/RIGHTS.ts | 3 + backend/src/auth/USER_RIGHTS.ts | 1 + .../src/graphql/input/ProjectBrandingInput.ts | 38 ++++++++++ backend/src/graphql/model/ProjectBranding.ts | 30 ++++++++ .../resolver/ProjectBrandingResolver.ts | 72 +++++++++++++++++++ .../ProjectBranding.ts | 8 +-- 8 files changed, 150 insertions(+), 4 deletions(-) create mode 100644 backend/src/graphql/input/ProjectBrandingInput.ts create mode 100644 backend/src/graphql/model/ProjectBranding.ts create mode 100644 backend/src/graphql/resolver/ProjectBrandingResolver.ts diff --git a/backend/src/auth/ADMIN_RIGHTS.ts b/backend/src/auth/ADMIN_RIGHTS.ts index e95935fd0..9ba3e7ccd 100644 --- a/backend/src/auth/ADMIN_RIGHTS.ts +++ b/backend/src/auth/ADMIN_RIGHTS.ts @@ -8,4 +8,5 @@ export const ADMIN_RIGHTS = [ RIGHTS.COMMUNITY_BY_UUID, RIGHTS.COMMUNITY_BY_IDENTIFIER, RIGHTS.HOME_COMMUNITY, + RIGHTS.PROJECT_BRANDING_MUTATE, ] diff --git a/backend/src/auth/INALIENABLE_RIGHTS.ts b/backend/src/auth/INALIENABLE_RIGHTS.ts index 25a4fa76d..c3c96b95e 100644 --- a/backend/src/auth/INALIENABLE_RIGHTS.ts +++ b/backend/src/auth/INALIENABLE_RIGHTS.ts @@ -9,4 +9,5 @@ export const INALIENABLE_RIGHTS = [ RIGHTS.QUERY_TRANSACTION_LINK, RIGHTS.QUERY_OPT_IN, RIGHTS.CHECK_USERNAME, + RIGHTS.PROJECT_BRANDING_BANNER, ] diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts index 1f0bda2b3..28ca70e79 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -8,6 +8,7 @@ export enum RIGHTS { QUERY_TRANSACTION_LINK = 'QUERY_TRANSACTION_LINK', QUERY_OPT_IN = 'QUERY_OPT_IN', CHECK_USERNAME = 'CHECK_USERNAME', + PROJECT_BRANDING_BANNER = 'PROJECT_BRANDING_BANNER', // User VERIFY_LOGIN = 'VERIFY_LOGIN', BALANCE = 'BALANCE', @@ -39,6 +40,7 @@ export enum RIGHTS { USER = 'USER', GMS_USER_PLAYGROUND = 'GMS_USER_PLAYGROUND', HUMHUB_AUTO_LOGIN = 'HUMHUB_AUTO_LOGIN', + PROJECT_BRANDING_VIEW = 'PROJECT_BRANDING_VIEW', // Moderator SEARCH_USERS = 'SEARCH_USERS', ADMIN_CREATE_CONTRIBUTION = 'ADMIN_CREATE_CONTRIBUTION', @@ -64,4 +66,5 @@ export enum RIGHTS { COMMUNITY_BY_IDENTIFIER = 'COMMUNITY_BY_IDENTIFIER', HOME_COMMUNITY = 'HOME_COMMUNITY', COMMUNITY_UPDATE = 'COMMUNITY_UPDATE', + PROJECT_BRANDING_MUTATE = 'PROJECT_BRANDING_MUTATE', } diff --git a/backend/src/auth/USER_RIGHTS.ts b/backend/src/auth/USER_RIGHTS.ts index de8e54af1..44f600a16 100644 --- a/backend/src/auth/USER_RIGHTS.ts +++ b/backend/src/auth/USER_RIGHTS.ts @@ -31,4 +31,5 @@ export const USER_RIGHTS = [ RIGHTS.USER, RIGHTS.GMS_USER_PLAYGROUND, RIGHTS.HUMHUB_AUTO_LOGIN, + RIGHTS.PROJECT_BRANDING_VIEW, ] diff --git a/backend/src/graphql/input/ProjectBrandingInput.ts b/backend/src/graphql/input/ProjectBrandingInput.ts new file mode 100644 index 000000000..cf5459b49 --- /dev/null +++ b/backend/src/graphql/input/ProjectBrandingInput.ts @@ -0,0 +1,38 @@ +import { IsString, IsOptional, MaxLength, IsNumber, IsBoolean, IsUrl } from 'class-validator' +import { InputType, Field, Int } from 'type-graphql' + +@InputType() +export class ProjectBrandingInput { + @Field(() => Int, { nullable: true }) + @IsOptional() + id: number | null | undefined + + @Field(() => String) + @IsString() + name: string + + @Field(() => String) + @IsString() + @MaxLength(32) + alias: string + + @Field(() => String, { nullable: true }) + @IsOptional() + @IsString() + description: string | null | undefined + + @Field(() => Int, { nullable: true }) + @IsNumber() + @IsOptional() + spaceId: number | null | undefined + + @Field(() => Boolean) + @IsBoolean() + newUserToSpace: boolean + + @Field(() => String, { nullable: true }) + @IsOptional() + @IsString() + @IsUrl() + logoUrl: string | null | undefined +} diff --git a/backend/src/graphql/model/ProjectBranding.ts b/backend/src/graphql/model/ProjectBranding.ts new file mode 100644 index 000000000..6f53b357a --- /dev/null +++ b/backend/src/graphql/model/ProjectBranding.ts @@ -0,0 +1,30 @@ +import { ProjectBranding as dbProjectBranding } from '@entity/ProjectBranding' +import { ObjectType, Field, Int } from 'type-graphql' + +@ObjectType() +export class ProjectBranding { + constructor(projectBranding: dbProjectBranding) { + Object.assign(this, projectBranding) + } + + @Field(() => Int) + id: number + + @Field(() => String) + name: string + + @Field(() => String) + alias: string + + @Field(() => String, { nullable: true }) + description: string | null + + @Field(() => Int, { nullable: true }) + spaceId: number | null + + @Field(() => Boolean) + newUserToSpace: boolean + + @Field(() => String, { nullable: true }) + logoUrl: string | null +} diff --git a/backend/src/graphql/resolver/ProjectBrandingResolver.ts b/backend/src/graphql/resolver/ProjectBrandingResolver.ts new file mode 100644 index 000000000..82b433283 --- /dev/null +++ b/backend/src/graphql/resolver/ProjectBrandingResolver.ts @@ -0,0 +1,72 @@ +import { ProjectBranding as DbProjectBranding } from '@entity/ProjectBranding' +import { Resolver, Query, Mutation, Arg, Int, Authorized } from 'type-graphql' + +import { ProjectBrandingInput } from '@input/ProjectBrandingInput' +import { ProjectBranding } from '@model/ProjectBranding' + +import { RIGHTS } from '@/auth/RIGHTS' +import { LogError } from '@/server/LogError' +import { backendLogger as logger } from '@/server/logger' + +@Resolver() +export class ProjectBrandingResolver { + @Query(() => [ProjectBranding]) + @Authorized([RIGHTS.PROJECT_BRANDING_VIEW]) + async getProjectBrandings(): Promise { + return (await DbProjectBranding.find()).map( + (entity: DbProjectBranding) => new ProjectBranding(entity), + ) + } + + @Query(() => ProjectBranding, { nullable: true }) + @Authorized([RIGHTS.PROJECT_BRANDING_VIEW]) + async getProjectBranding(@Arg('id', () => Int) id: number): Promise { + const projectBrandingEntity = await DbProjectBranding.findOneBy({ id }) + if (!projectBrandingEntity) { + throw new LogError(`Project Branding with id: ${id} not found`) + } + return new ProjectBranding(projectBrandingEntity) + } + + @Query(() => String, { nullable: true }) + @Authorized([RIGHTS.PROJECT_BRANDING_BANNER]) + async getProjectBrandingBanner( + @Arg('alias', () => String) alias: string, + ): Promise { + const projectBrandingEntity = await DbProjectBranding.findOne({ + where: { alias }, + select: { logoUrl: true }, + }) + if (!projectBrandingEntity) { + throw new LogError(`Project Branding with alias: ${alias} not found`) + } + return projectBrandingEntity.logoUrl + } + + @Mutation(() => ProjectBranding, { nullable: true }) + @Authorized([RIGHTS.PROJECT_BRANDING_MUTATE]) + async upsertProjectBranding( + @Arg('data') data: ProjectBrandingInput, + ): Promise { + const projectBranding = data.id + ? await DbProjectBranding.findOneOrFail({ where: { id: data.id } }) + : new DbProjectBranding() + + Object.assign(projectBranding, data) + await projectBranding.save() + + return new ProjectBranding(projectBranding) + } + + @Mutation(() => Boolean) + @Authorized([RIGHTS.PROJECT_BRANDING_MUTATE]) + async deleteProjectBranding(@Arg('id', () => Int) id: number): Promise { + try { + await DbProjectBranding.delete({ id }) + return true + } catch (err) { + logger.error(err) + return false + } + } +} diff --git a/database/entity/0088-create_project_brandings/ProjectBranding.ts b/database/entity/0088-create_project_brandings/ProjectBranding.ts index 9fb53e32b..4757904a3 100644 --- a/database/entity/0088-create_project_brandings/ProjectBranding.ts +++ b/database/entity/0088-create_project_brandings/ProjectBranding.ts @@ -1,8 +1,8 @@ -import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm' +import { Entity, PrimaryGeneratedColumn, Column, BaseEntity } from 'typeorm' @Entity('project_brandings') -export class ProjectBranding { - @PrimaryGeneratedColumn() +export class ProjectBranding extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) id: number @Column({ name: 'name', type: 'varchar', length: 255 }) @@ -17,7 +17,7 @@ export class ProjectBranding { @Column({ name: 'space_id', type: 'int', unsigned: true, nullable: true, default: null }) spaceId: number | null - @Column({ name:'new_user_to_space', type: 'tinyint', width: 1, default: 0 }) + @Column({ name: 'new_user_to_space', type: 'tinyint', width: 1, default: 0 }) newUserToSpace: boolean @Column({ name: 'logo_url', type: 'varchar', length: 255, nullable: true, default: null })