mirror of
https://github.com/sct/overseerr.git
synced 2025-10-02 00:33:09 +02:00
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:
@@ -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()
|
||||
|
@@ -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"`);
|
||||
}
|
||||
}
|
@@ -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',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user