Skip to content

Commit

Permalink
Add a moderator (#566)
Browse files Browse the repository at this point in the history
  • Loading branch information
antoinejaussoin authored Sep 24, 2023
1 parent cdc4150 commit a90b3f0
Show file tree
Hide file tree
Showing 53 changed files with 792 additions and 502 deletions.
9 changes: 2 additions & 7 deletions backend/src/common/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,9 @@ const actions = {
ADD_POST_GROUP_SUCCESS: 'retrospected/group/add/success',
DELETE_POST_GROUP: 'retrospected/group/delete',
EDIT_POST_GROUP: 'retrospected/group/edit',
RENAME_SESSION: 'retrospected/session/rename',
JOIN_SESSION: 'retrospected/session/join',
LEAVE_SESSION: 'retrospected/session/leave',
EDIT_OPTIONS: 'retrospected/session/options/edit',
EDIT_COLUMNS: 'retrospected/session/columns/edit',
SAVE_TEMPLATE: 'retrospected/session/template/save',
SAVE_SESSION_SETTINGS: 'retrospected/session/settings/save',
LOCK_SESSION: 'retrospected/session/lock',
REQUEST_BOARD: 'retrospected/session/request',
USER_READY: 'retrospected/user-ready',
Expand All @@ -32,10 +29,8 @@ const actions = {
RECEIVE_DELETE_POST_GROUP: 'retrospected/group/receive/delete',
RECEIVE_EDIT_POST_GROUP: 'retrospected/group/receive/edit',
RECEIVE_BOARD: 'retrospected/posts/receive-all',
RECEIVE_OPTIONS: 'retrospected/session/options/receive',
RECEIVE_COLUMNS: 'retrospected/session/columns/receive',
RECEIVE_SESSION_SETTINGS: 'retrospected/session/settings/receive',
RECEIVE_CLIENT_LIST: 'retrospected/session/receive/client-list',
RECEIVE_SESSION_NAME: 'retrospected/session/receive/rename',
RECEIVE_LOCK_SESSION: 'retrospected/session/receive/lock',
RECEIVE_UNAUTHORIZED: 'retrospected/session/receive/unauthorized',
RECEIVE_RATE_LIMITED: 'retrospected/rate-limited-error',
Expand Down
8 changes: 4 additions & 4 deletions backend/src/common/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ export const defaultOptions: SessionOptions = {
allowTimer: true,
timerDuration: 15 * 60,
readonlyOnTimerEnd: true,
restrictTitleEditToOwner: false,
restrictReorderingToOwner: false,
restrictGroupingToOwner: false,
restrictTitleEditToModerator: false,
restrictReorderingToModerator: false,
restrictGroupingToModerator: false,
};

export const defaultSession: Omit<Session, 'createdBy'> = {
export const defaultSession: Omit<Session, 'createdBy' | 'moderator'> = {
id: '',
columns: [
{ id: '', index: 0, label: '', type: 'well', color: '', icon: null },
Expand Down
23 changes: 15 additions & 8 deletions backend/src/common/types.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
export interface Session extends PostContainer, Entity {
export type SessionSettings = Partial<AllSessionSettings>;

export interface AllSessionSettings {
name: string | null;
moderator: User;
options: SessionOptions;
columns: ColumnDefinition[];
locked: boolean;
timer: Date | null;
}

export interface Session extends AllSessionSettings, PostContainer, Entity {
posts: Post[];
groups: PostGroup[];
columns: ColumnDefinition[];
messages: Message[];
options: SessionOptions;
encrypted: string | null;
locked: boolean;
createdBy: User;
ready: string[];
timer: Date | null;
demo: boolean;
}

Expand Down Expand Up @@ -60,9 +66,9 @@ export interface SessionOptions {
allowGrouping: boolean;
allowReordering: boolean;
allowCancelVote: boolean;
restrictTitleEditToOwner: boolean;
restrictReorderingToOwner: boolean;
restrictGroupingToOwner: boolean;
restrictTitleEditToModerator: boolean;
restrictReorderingToModerator: boolean;
restrictGroupingToModerator: boolean;
blurCards: boolean;
newPostsFirst: boolean;
allowTimer: boolean;
Expand Down Expand Up @@ -211,6 +217,7 @@ export type TrackingEvent =
| 'home/load-previous'
| 'game/session/edit-options'
| 'game/session/edit-columns'
| 'game/session/save-options'
| 'game/session/reset'
| 'game/session/disconnect'
| 'game/session/unexpected-disconnection'
Expand Down
16 changes: 6 additions & 10 deletions backend/src/common/ws.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import {
ColumnDefinition,
Post,
PostGroup,
SessionOptions,
SessionSettings,
User,
VoteExtract,
VoteType,
Expand All @@ -18,10 +17,6 @@ export interface WsUserData {
user: User;
}

export interface WsNameData {
name: string;
}

export interface WsPostUpdatePayload {
post: Omit<Omit<Omit<Post, 'votes'>, 'user'>, 'group'>;
groupId: string | null;
Expand Down Expand Up @@ -58,9 +53,9 @@ export interface WsDeleteGroupPayload {
groupId: string;
}

export interface WsSaveTemplatePayload {
columns: ColumnDefinition[];
options: SessionOptions;
export interface WsSaveSessionSettingsPayload {
settings: SessionSettings;
saveAsTemplate: boolean;
}

export interface WsUserReadyPayload {
Expand All @@ -84,7 +79,8 @@ export type WsErrorType =
| 'cannot_record_chat_message'
| 'cannot_cancel_votes'
| 'unknown_error'
| 'action_unauthorised';
| 'action_unauthorised'
| 'cannot_save_session_settings';

export interface WsErrorPayload {
type: WsErrorType;
Expand Down
26 changes: 26 additions & 0 deletions backend/src/db/actions/sessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,32 @@ export async function updateName(
});
}

export async function updateModerator(
sessionId: string,
moderatorId: string
): Promise<boolean> {
return await transaction(async (manager) => {
try {
const sessionRepository = manager.withRepository(SessionRepository);
const session = await sessionRepository.findOne({
where: { id: sessionId },
});
if (session) {
await sessionRepository.save({
...session,
moderator: {
id: moderatorId,
},
});
return true;
}
return false;
} catch {
return false;
}
});
}

export async function getSessionWithVisitors(
sessionId: string
): Promise<SessionEntity | null> {
Expand Down
11 changes: 10 additions & 1 deletion backend/src/db/actions/users.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { UserEntity, UserView } from '../entities/index.js';
import { EntityManager, Not } from 'typeorm';
import { EntityManager, In, Not } from 'typeorm';
import {
UserIdentityRepository,
UserRepository,
Expand Down Expand Up @@ -88,6 +88,15 @@ export async function getUserViewInner(
return user || null;
}

export async function getRelatedUsers(userId: string): Promise<UserView[]> {
return await transaction(async (manager) => {
const userRepository = manager.withRepository(UserRepository);
const ids = await userRepository.getRelatedUsersIds(userId);
const userViewRepository = manager.getRepository(UserView);
return userViewRepository.findBy({ id: In(ids) });
});
}

export async function getPasswordIdentity(
username: string
): Promise<UserIdentityEntity | null> {
Expand Down
5 changes: 5 additions & 0 deletions backend/src/db/entities/Session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ export default class SessionEntity {
@ManyToOne(() => UserEntity, { eager: true, cascade: true, nullable: false })
@Index()
public createdBy: UserEntity;
@ManyToOne(() => UserEntity, { eager: true, cascade: true, nullable: false })
@Index()
public moderator: UserEntity;
@OneToMany(() => PostEntity, (post) => post.session, {
cascade: true,
nullable: false,
Expand Down Expand Up @@ -80,6 +83,7 @@ export default class SessionEntity {
columns:
this.columns === undefined ? [] : this.columns.map((c) => c.toJson()),
createdBy: this.createdBy.toJson(),
moderator: this.moderator.toJson(),
groups:
this.groups === undefined ? [] : this.groups.map((g) => g.toJson()),
id: this.id,
Expand All @@ -105,6 +109,7 @@ export default class SessionEntity {
this.id = id;
this.name = name;
this.createdBy = createdBy;
this.moderator = createdBy;
this.options = new SessionOptionsEntity(options);
this.encrypted = null;
this.locked = false;
Expand Down
16 changes: 9 additions & 7 deletions backend/src/db/entities/SessionOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ export default class SessionOptionsEntity {
@Column({ default: true })
public allowTimer: boolean;
@Column({ default: false })
public restrictTitleEditToOwner: boolean;
public restrictTitleEditToModerator: boolean;
@Column({ default: false })
public restrictReorderingToOwner: boolean;
public restrictReorderingToModerator: boolean;
@Column({ default: false })
public restrictGroupingToOwner: boolean;
public restrictGroupingToModerator: boolean;
@Column({ type: 'numeric', default: 15 * 60 })
public timerDuration: number;
@Column({ default: true })
Expand Down Expand Up @@ -69,10 +69,12 @@ export default class SessionOptionsEntity {
this.allowTimer = optionsWithDefault.allowTimer;
this.timerDuration = optionsWithDefault.timerDuration;
this.readonlyOnTimerEnd = optionsWithDefault.readonlyOnTimerEnd;
this.restrictTitleEditToOwner = optionsWithDefault.restrictTitleEditToOwner;
this.restrictReorderingToOwner =
optionsWithDefault.restrictReorderingToOwner;
this.restrictGroupingToOwner = optionsWithDefault.restrictGroupingToOwner;
this.restrictTitleEditToModerator =
optionsWithDefault.restrictTitleEditToModerator;
this.restrictReorderingToModerator =
optionsWithDefault.restrictReorderingToModerator;
this.restrictGroupingToModerator =
optionsWithDefault.restrictGroupingToModerator;
}
}

Expand Down
19 changes: 19 additions & 0 deletions backend/src/db/migrations/1689787853666-Moderator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { MigrationInterface, QueryRunner } from "typeorm";

export class Moderator1689787853666 implements MigrationInterface {
name = 'Moderator1689787853666'

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "sessions" ADD "moderator_id" character varying`);
await queryRunner.query(`CREATE INDEX "IDX_7127e358f4f0bf8686d77af14c" ON "sessions" ("moderator_id") `);
await queryRunner.query(`ALTER TABLE "sessions" ADD CONSTRAINT "FK_7127e358f4f0bf8686d77af14cd" FOREIGN KEY ("moderator_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`UPDATE "sessions" SET "moderator_id" = "created_by_id"`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "sessions" DROP CONSTRAINT "FK_7127e358f4f0bf8686d77af14cd"`);
await queryRunner.query(`DROP INDEX "public"."IDX_7127e358f4f0bf8686d77af14c"`);
await queryRunner.query(`ALTER TABLE "sessions" DROP COLUMN "moderator_id"`);
}

}
18 changes: 18 additions & 0 deletions backend/src/db/migrations/1689787951867-ModeratorNonNull.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { MigrationInterface, QueryRunner } from "typeorm";

export class ModeratorNonNull1689787951867 implements MigrationInterface {
name = 'ModeratorNonNull1689787951867'

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "sessions" DROP CONSTRAINT "FK_1ccf045da14e5350b26ee882592"`);
await queryRunner.query(`ALTER TABLE "sessions" ALTER COLUMN "created_by_id" DROP NOT NULL`);
await queryRunner.query(`ALTER TABLE "sessions" ADD CONSTRAINT "FK_1ccf045da14e5350b26ee882592" FOREIGN KEY ("created_by_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "sessions" DROP CONSTRAINT "FK_1ccf045da14e5350b26ee882592"`);
await queryRunner.query(`ALTER TABLE "sessions" ALTER COLUMN "created_by_id" SET NOT NULL`);
await queryRunner.query(`ALTER TABLE "sessions" ADD CONSTRAINT "FK_1ccf045da14e5350b26ee882592" FOREIGN KEY ("created_by_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
}

}
66 changes: 66 additions & 0 deletions backend/src/db/migrations/1695539888278-RenameModerator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { MigrationInterface, QueryRunner } from "typeorm";

export class RenameModerator1695539888278 implements MigrationInterface {
name = 'RenameModerator1695539888278'

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "templates" ADD "options_restrict_title_edit_to_moderator" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "templates" ADD "options_restrict_reordering_to_moderator" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "templates" ADD "options_restrict_grouping_to_moderator" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "sessions" ADD "options_restrict_title_edit_to_moderator" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "sessions" ADD "options_restrict_reordering_to_moderator" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "sessions" ADD "options_restrict_grouping_to_moderator" boolean NOT NULL DEFAULT false`);

await queryRunner.query(`UPDATE "templates" SET
options_restrict_title_edit_to_moderator = options_restrict_title_edit_to_owner,
options_restrict_reordering_to_moderator = options_restrict_reordering_to_owner,
options_restrict_grouping_to_moderator = options_restrict_grouping_to_owner
`);
await queryRunner.query(`UPDATE "sessions" SET
options_restrict_title_edit_to_moderator = options_restrict_title_edit_to_owner,
options_restrict_reordering_to_moderator = options_restrict_reordering_to_owner,
options_restrict_grouping_to_moderator = options_restrict_grouping_to_owner
`);
await queryRunner.query(`ALTER TABLE "templates" DROP COLUMN "options_restrict_title_edit_to_owner"`);
await queryRunner.query(`ALTER TABLE "templates" DROP COLUMN "options_restrict_reordering_to_owner"`);
await queryRunner.query(`ALTER TABLE "templates" DROP COLUMN "options_restrict_grouping_to_owner"`);
await queryRunner.query(`ALTER TABLE "sessions" DROP COLUMN "options_restrict_title_edit_to_owner"`);
await queryRunner.query(`ALTER TABLE "sessions" DROP COLUMN "options_restrict_reordering_to_owner"`);
await queryRunner.query(`ALTER TABLE "sessions" DROP COLUMN "options_restrict_grouping_to_owner"`);
await queryRunner.query(`ALTER TABLE "sessions" DROP CONSTRAINT "FK_1ccf045da14e5350b26ee882592"`);
await queryRunner.query(`ALTER TABLE "sessions" ALTER COLUMN "created_by_id" SET NOT NULL`);
await queryRunner.query(`ALTER TABLE "sessions" ADD CONSTRAINT "FK_1ccf045da14e5350b26ee882592" FOREIGN KEY ("created_by_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "sessions" DROP CONSTRAINT "FK_1ccf045da14e5350b26ee882592"`);
await queryRunner.query(`ALTER TABLE "sessions" ALTER COLUMN "created_by_id" DROP NOT NULL`);
await queryRunner.query(`ALTER TABLE "sessions" ADD CONSTRAINT "FK_1ccf045da14e5350b26ee882592" FOREIGN KEY ("created_by_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "sessions" ADD "options_restrict_grouping_to_owner" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "sessions" ADD "options_restrict_reordering_to_owner" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "sessions" ADD "options_restrict_title_edit_to_owner" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "templates" ADD "options_restrict_grouping_to_owner" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "templates" ADD "options_restrict_reordering_to_owner" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "templates" ADD "options_restrict_title_edit_to_owner" boolean NOT NULL DEFAULT false`);

await queryRunner.query(`UPDATE "templates" SET
options_restrict_title_edit_to_owner = options_restrict_title_edit_to_moderator,
options_restrict_reordering_to_owner = options_restrict_reordering_to_moderator,
options_restrict_grouping_to_owner = options_restrict_grouping_to_moderator
`);
await queryRunner.query(`UPDATE "sessions" SET
options_restrict_title_edit_to_owner = options_restrict_title_edit_to_moderator,
options_restrict_reordering_to_owner = options_restrict_reordering_to_moderator,
options_restrict_grouping_to_owner = options_restrict_grouping_to_moderator
`);

await queryRunner.query(`ALTER TABLE "sessions" DROP COLUMN "options_restrict_grouping_to_moderator"`);
await queryRunner.query(`ALTER TABLE "sessions" DROP COLUMN "options_restrict_reordering_to_moderator"`);
await queryRunner.query(`ALTER TABLE "sessions" DROP COLUMN "options_restrict_title_edit_to_moderator"`);
await queryRunner.query(`ALTER TABLE "templates" DROP COLUMN "options_restrict_grouping_to_moderator"`);
await queryRunner.query(`ALTER TABLE "templates" DROP COLUMN "options_restrict_reordering_to_moderator"`);
await queryRunner.query(`ALTER TABLE "templates" DROP COLUMN "options_restrict_title_edit_to_moderator"`);

}

}
18 changes: 18 additions & 0 deletions backend/src/db/migrations/1695540515122-ModeratorNonNull.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { MigrationInterface, QueryRunner } from "typeorm";

export class ModeratorNonNull1695540515122 implements MigrationInterface {
name = 'ModeratorNonNull1695540515122'

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "sessions" DROP CONSTRAINT "FK_7127e358f4f0bf8686d77af14cd"`);
await queryRunner.query(`ALTER TABLE "sessions" ALTER COLUMN "moderator_id" SET NOT NULL`);
await queryRunner.query(`ALTER TABLE "sessions" ADD CONSTRAINT "FK_7127e358f4f0bf8686d77af14cd" FOREIGN KEY ("moderator_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "sessions" DROP CONSTRAINT "FK_7127e358f4f0bf8686d77af14cd"`);
await queryRunner.query(`ALTER TABLE "sessions" ALTER COLUMN "moderator_id" DROP NOT NULL`);
await queryRunner.query(`ALTER TABLE "sessions" ADD CONSTRAINT "FK_7127e358f4f0bf8686d77af14cd" FOREIGN KEY ("moderator_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
}

}
3 changes: 2 additions & 1 deletion backend/src/db/repositories/SessionRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@ export default getBaseRepository(SessionEntity).extend({
}
},
async saveFromJson(
session: Omit<JsonSession, 'createdBy'>,
session: Omit<JsonSession, 'createdBy' | 'moderator'>,
authorId: string
): Promise<JsonSession> {
const sessionWithoutPosts = {
...session,
posts: undefined,
columns: undefined,
createdBy: { id: authorId },
moderator: { id: authorId },
};
delete sessionWithoutPosts.posts;
delete sessionWithoutPosts.columns;
Expand Down
Loading

0 comments on commit a90b3f0

Please sign in to comment.