feat(frontend): added user deletion to the user list

also includes small updates to the api to prevent administrators from being deleted, as well as
migrations to cascade deletions to requests the users made

fixes #348
This commit is contained in:
sct
2020-12-17 16:03:27 +00:00
parent ee84f74f8a
commit 727fa06c18
7 changed files with 175 additions and 8 deletions

View File

@@ -36,10 +36,18 @@ export class MediaRequest {
})
public media: Media;
@ManyToOne(() => User, (user) => user.requests, { eager: true })
@ManyToOne(() => User, (user) => user.requests, {
eager: true,
onDelete: 'CASCADE',
})
public requestedBy: User;
@ManyToOne(() => User, { nullable: true, cascade: true, eager: true })
@ManyToOne(() => User, {
nullable: true,
cascade: true,
eager: true,
onDelete: 'SET NULL',
})
public modifiedBy?: User;
@CreateDateColumn()

View File

@@ -0,0 +1,32 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddUserRequestDeleteCascades1608219049304
implements MigrationInterface {
name = 'AddUserRequestDeleteCascades1608219049304';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "temporary_media_request" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "status" integer NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "type" varchar NOT NULL, "mediaId" integer, "requestedById" integer, "modifiedById" integer, CONSTRAINT "FK_a1aa713f41c99e9d10c48da75a0" FOREIGN KEY ("mediaId") REFERENCES "media" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_6997bee94720f1ecb7f31137095" FOREIGN KEY ("requestedById") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_f4fc4efa14c3ba2b29c4525fa15" FOREIGN KEY ("modifiedById") REFERENCES "user" ("id") ON DELETE SET NULL ON UPDATE NO ACTION)`
);
await queryRunner.query(
`INSERT INTO "temporary_media_request"("id", "status", "createdAt", "updatedAt", "type", "mediaId", "requestedById", "modifiedById") SELECT "id", "status", "createdAt", "updatedAt", "type", "mediaId", "requestedById", "modifiedById" FROM "media_request"`
);
await queryRunner.query(`DROP TABLE "media_request"`);
await queryRunner.query(
`ALTER TABLE "temporary_media_request" RENAME TO "media_request"`
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "media_request" RENAME TO "temporary_media_request"`
);
await queryRunner.query(
`CREATE TABLE "media_request" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "status" integer NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "type" varchar NOT NULL, "mediaId" integer, "requestedById" integer, "modifiedById" integer, CONSTRAINT "FK_a1aa713f41c99e9d10c48da75a0" FOREIGN KEY ("mediaId") REFERENCES "media" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_6997bee94720f1ecb7f31137095" FOREIGN KEY ("requestedById") REFERENCES "user" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_f4fc4efa14c3ba2b29c4525fa15" FOREIGN KEY ("modifiedById") REFERENCES "user" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`
);
await queryRunner.query(
`INSERT INTO "media_request"("id", "status", "createdAt", "updatedAt", "type", "mediaId", "requestedById", "modifiedById") SELECT "id", "status", "createdAt", "updatedAt", "type", "mediaId", "requestedById", "modifiedById" FROM "temporary_media_request"`
);
await queryRunner.query(`DROP TABLE "temporary_media_request"`);
}
}

View File

@@ -1,7 +1,9 @@
import { Router } from 'express';
import { getRepository } from 'typeorm';
import { MediaRequest } from '../entity/MediaRequest';
import { User } from '../entity/User';
import { hasPermission, Permission } from '../lib/permissions';
import logger from '../logger';
const router = Router();
@@ -94,13 +96,49 @@ router.delete<{ id: string }>('/:id', async (req, res, next) => {
try {
const userRepository = getRepository(User);
const user = await userRepository.findOneOrFail({
const user = await userRepository.findOne({
where: { id: Number(req.params.id) },
relations: ['requests'],
});
if (!user) {
return next({ status: 404, message: 'User not found' });
}
if (user.id === 1) {
return next({ status: 405, message: 'This account cannot be deleted.' });
}
if (user.hasPermission(Permission.ADMIN)) {
return next({
status: 405,
message: 'You cannot delete users with administrative privileges.',
});
}
const requestRepository = getRepository(MediaRequest);
/**
* Requests are usually deleted through a cascade constraint. Those however, do
* not trigger the removal event so listeners to not run and the parent Media
* will not be updated back to unknown for titles that were still pending. So
* we manually remove all requests from the user here so the parent media's
* properly reflect the change.
*/
await requestRepository.remove(user.requests);
await userRepository.delete(user.id);
return res.status(200).json(user.filter());
} catch (e) {
next({ status: 404, message: 'User not found' });
logger.error('Something went wrong deleting a user', {
label: 'API',
userId: req.params.id,
errorMessage: e.message,
});
return next({
status: 500,
message: 'Something went wrong deleting the user',
});
}
});