feat: add soft-delete functionality for categories, areas, and teams in Admin service
This commit is contained in:
@@ -138,6 +138,20 @@ void AdminController::updateCategory(
|
||||
std::move(callback));
|
||||
}
|
||||
|
||||
void AdminController::deleteCategory(
|
||||
const drogon::HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
long long categoryId)
|
||||
{
|
||||
auto user = requireUser(req, callback);
|
||||
if (!user || blockPasswordChangeRequired(*user, callback))
|
||||
{
|
||||
return;
|
||||
}
|
||||
visione::services::AdminService service;
|
||||
sendAdmin(service.deleteCategory(*user, categoryId), std::move(callback));
|
||||
}
|
||||
|
||||
void AdminController::updateArea(
|
||||
const drogon::HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
@@ -153,6 +167,20 @@ void AdminController::updateArea(
|
||||
std::move(callback));
|
||||
}
|
||||
|
||||
void AdminController::deleteArea(
|
||||
const drogon::HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
long long areaId)
|
||||
{
|
||||
auto user = requireUser(req, callback);
|
||||
if (!user || blockPasswordChangeRequired(*user, callback))
|
||||
{
|
||||
return;
|
||||
}
|
||||
visione::services::AdminService service;
|
||||
sendAdmin(service.deleteArea(*user, areaId), std::move(callback));
|
||||
}
|
||||
|
||||
void AdminController::updateTeam(
|
||||
const drogon::HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
@@ -168,6 +196,20 @@ void AdminController::updateTeam(
|
||||
std::move(callback));
|
||||
}
|
||||
|
||||
void AdminController::deleteTeam(
|
||||
const drogon::HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
long long teamId)
|
||||
{
|
||||
auto user = requireUser(req, callback);
|
||||
if (!user || blockPasswordChangeRequired(*user, callback))
|
||||
{
|
||||
return;
|
||||
}
|
||||
visione::services::AdminService service;
|
||||
sendAdmin(service.deleteTeam(*user, teamId), std::move(callback));
|
||||
}
|
||||
|
||||
void AdminController::overviewStats(
|
||||
const drogon::HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback)
|
||||
|
||||
@@ -14,12 +14,21 @@ class AdminController : public drogon::HttpController<AdminController>
|
||||
ADD_METHOD_TO(AdminController::updateCategory,
|
||||
"/api/v1/admin/categories/{1}",
|
||||
drogon::Patch);
|
||||
ADD_METHOD_TO(AdminController::deleteCategory,
|
||||
"/api/v1/admin/categories/{1}",
|
||||
drogon::Delete);
|
||||
ADD_METHOD_TO(AdminController::updateArea,
|
||||
"/api/v1/admin/areas/{1}",
|
||||
drogon::Patch);
|
||||
ADD_METHOD_TO(AdminController::deleteArea,
|
||||
"/api/v1/admin/areas/{1}",
|
||||
drogon::Delete);
|
||||
ADD_METHOD_TO(AdminController::updateTeam,
|
||||
"/api/v1/admin/teams/{1}",
|
||||
drogon::Patch);
|
||||
ADD_METHOD_TO(AdminController::deleteTeam,
|
||||
"/api/v1/admin/teams/{1}",
|
||||
drogon::Delete);
|
||||
ADD_METHOD_TO(AdminController::overviewStats,
|
||||
"/api/v1/admin/stats/overview",
|
||||
drogon::Get);
|
||||
@@ -51,14 +60,26 @@ class AdminController : public drogon::HttpController<AdminController>
|
||||
const drogon::HttpRequestPtr &req,
|
||||
std::function<void(const drogon::HttpResponsePtr &)> &&callback,
|
||||
long long categoryId);
|
||||
void deleteCategory(
|
||||
const drogon::HttpRequestPtr &req,
|
||||
std::function<void(const drogon::HttpResponsePtr &)> &&callback,
|
||||
long long categoryId);
|
||||
void updateArea(
|
||||
const drogon::HttpRequestPtr &req,
|
||||
std::function<void(const drogon::HttpResponsePtr &)> &&callback,
|
||||
long long areaId);
|
||||
void deleteArea(
|
||||
const drogon::HttpRequestPtr &req,
|
||||
std::function<void(const drogon::HttpResponsePtr &)> &&callback,
|
||||
long long areaId);
|
||||
void updateTeam(
|
||||
const drogon::HttpRequestPtr &req,
|
||||
std::function<void(const drogon::HttpResponsePtr &)> &&callback,
|
||||
long long teamId);
|
||||
void deleteTeam(
|
||||
const drogon::HttpRequestPtr &req,
|
||||
std::function<void(const drogon::HttpResponsePtr &)> &&callback,
|
||||
long long teamId);
|
||||
void overviewStats(
|
||||
const drogon::HttpRequestPtr &req,
|
||||
std::function<void(const drogon::HttpResponsePtr &)> &&callback);
|
||||
|
||||
@@ -111,11 +111,14 @@ Authorization: Bearer <token>
|
||||
## Admin Reference Data
|
||||
- `POST /admin/categories`
|
||||
- `PATCH /admin/categories/{id}`
|
||||
- `DELETE /admin/categories/{id}`
|
||||
- `POST /admin/areas`
|
||||
- `PATCH /admin/areas/{id}`
|
||||
- `DELETE /admin/areas/{id}`
|
||||
area polygon must stay inside allowed Bari municipal boundary
|
||||
- `POST /admin/teams`
|
||||
- `PATCH /admin/teams/{id}`
|
||||
- `DELETE /admin/teams/{id}`
|
||||
- `GET /admin/users`
|
||||
- `PATCH /admin/users/{id}/roles`
|
||||
- `PATCH /admin/users/{id}/ban`
|
||||
|
||||
@@ -92,6 +92,7 @@ Schema truth is `migrations/*.sql`. This file is compact human map of current DB
|
||||
- `reward_events(report_id, reason)` unique
|
||||
- `api_tokens.token_hash` unique
|
||||
- `intervention_access_links.token_hash` unique
|
||||
- category/team/area delete behavior is soft delete via `active = FALSE`
|
||||
|
||||
## Current Query Model
|
||||
- No generated ORM models
|
||||
|
||||
@@ -79,9 +79,12 @@ moderate abuse, and inspect operational stats.
|
||||
|
||||
### Admin
|
||||
- create/update categories
|
||||
- soft-delete categories
|
||||
- create/update areas
|
||||
area polygons must stay inside Bari municipal boundary
|
||||
- soft-delete areas
|
||||
- create/update teams
|
||||
- soft-delete teams
|
||||
- list users, update roles, ban/unban
|
||||
- global stats including area/category/team resolution aggregates
|
||||
|
||||
|
||||
@@ -1712,6 +1712,51 @@
|
||||
}
|
||||
},
|
||||
"/api/v1/admin/areas/{id}": {
|
||||
"delete": {
|
||||
"description": "Soft-deletes an existing municipal area by setting active to false.",
|
||||
"operationId": "AdminController.deleteArea",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"format": "int64",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/OkResponse"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Deleted area result."
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#/components/responses/Unauthorized"
|
||||
},
|
||||
"403": {
|
||||
"$ref": "#/components/responses/Forbidden"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/components/responses/NotFound"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": [ ]
|
||||
}
|
||||
],
|
||||
"summary": "Delete area",
|
||||
"tags": [
|
||||
"Admin"
|
||||
]
|
||||
},
|
||||
"patch": {
|
||||
"description": "Updates an existing municipal area.",
|
||||
"operationId": "AdminController.updateArea",
|
||||
@@ -1816,6 +1861,51 @@
|
||||
}
|
||||
},
|
||||
"/api/v1/admin/categories/{id}": {
|
||||
"delete": {
|
||||
"description": "Soft-deletes an existing report category by setting active to false.",
|
||||
"operationId": "AdminController.deleteCategory",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"format": "int64",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/OkResponse"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Deleted category result."
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#/components/responses/Unauthorized"
|
||||
},
|
||||
"403": {
|
||||
"$ref": "#/components/responses/Forbidden"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/components/responses/NotFound"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": [ ]
|
||||
}
|
||||
],
|
||||
"summary": "Delete category",
|
||||
"tags": [
|
||||
"Admin"
|
||||
]
|
||||
},
|
||||
"patch": {
|
||||
"description": "Updates an existing report category.",
|
||||
"operationId": "AdminController.updateCategory",
|
||||
@@ -2075,6 +2165,51 @@
|
||||
}
|
||||
},
|
||||
"/api/v1/admin/teams/{id}": {
|
||||
"delete": {
|
||||
"description": "Soft-deletes an existing intervention team by setting active to false.",
|
||||
"operationId": "AdminController.deleteTeam",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"format": "int64",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/OkResponse"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Deleted team result."
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#/components/responses/Unauthorized"
|
||||
},
|
||||
"403": {
|
||||
"$ref": "#/components/responses/Forbidden"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/components/responses/NotFound"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": [ ]
|
||||
}
|
||||
],
|
||||
"summary": "Delete team",
|
||||
"tags": [
|
||||
"Admin"
|
||||
]
|
||||
},
|
||||
"patch": {
|
||||
"description": "Updates an existing intervention team.",
|
||||
"operationId": "AdminController.updateTeam",
|
||||
|
||||
@@ -374,6 +374,26 @@ Json::Value AdminService::updateModeration(const auth::UserContext &user,
|
||||
return success();
|
||||
}
|
||||
|
||||
Json::Value AdminService::deleteCategory(const auth::UserContext &user,
|
||||
long long categoryId)
|
||||
{
|
||||
if (!auth::isAdmin(user))
|
||||
{
|
||||
return failure("forbidden");
|
||||
}
|
||||
auto db = drogon::app().getDbClient();
|
||||
const auto rows = db->execSqlSync(
|
||||
"SELECT id FROM report_categories WHERE id = ?",
|
||||
categoryId);
|
||||
if (rows.empty())
|
||||
{
|
||||
return failure("category_not_found");
|
||||
}
|
||||
db->execSqlSync("UPDATE report_categories SET active = FALSE WHERE id = ?",
|
||||
categoryId);
|
||||
return success();
|
||||
}
|
||||
|
||||
Json::Value AdminService::updateCategory(const auth::UserContext &user,
|
||||
long long categoryId,
|
||||
const Json::Value &body)
|
||||
@@ -411,6 +431,23 @@ Json::Value AdminService::updateCategory(const auth::UserContext &user,
|
||||
return success();
|
||||
}
|
||||
|
||||
Json::Value AdminService::deleteArea(const auth::UserContext &user,
|
||||
long long areaId)
|
||||
{
|
||||
if (!auth::isAdmin(user))
|
||||
{
|
||||
return failure("forbidden");
|
||||
}
|
||||
auto db = drogon::app().getDbClient();
|
||||
const auto rows = db->execSqlSync("SELECT id FROM areas WHERE id = ?", areaId);
|
||||
if (rows.empty())
|
||||
{
|
||||
return failure("area_not_found");
|
||||
}
|
||||
db->execSqlSync("UPDATE areas SET active = FALSE WHERE id = ?", areaId);
|
||||
return success();
|
||||
}
|
||||
|
||||
Json::Value AdminService::updateArea(const auth::UserContext &user,
|
||||
long long areaId,
|
||||
const Json::Value &body)
|
||||
@@ -458,6 +495,23 @@ Json::Value AdminService::updateArea(const auth::UserContext &user,
|
||||
return success();
|
||||
}
|
||||
|
||||
Json::Value AdminService::deleteTeam(const auth::UserContext &user,
|
||||
long long teamId)
|
||||
{
|
||||
if (!auth::isAdmin(user))
|
||||
{
|
||||
return failure("forbidden");
|
||||
}
|
||||
auto db = drogon::app().getDbClient();
|
||||
const auto rows = db->execSqlSync("SELECT id FROM teams WHERE id = ?", teamId);
|
||||
if (rows.empty())
|
||||
{
|
||||
return failure("team_not_found");
|
||||
}
|
||||
db->execSqlSync("UPDATE teams SET active = FALSE WHERE id = ?", teamId);
|
||||
return success();
|
||||
}
|
||||
|
||||
Json::Value AdminService::updateTeam(const auth::UserContext &user,
|
||||
long long teamId,
|
||||
const Json::Value &body)
|
||||
|
||||
@@ -24,6 +24,9 @@ class AdminService
|
||||
Json::Value updateModeration(const auth::UserContext &user,
|
||||
long long flagId,
|
||||
const Json::Value &body);
|
||||
Json::Value deleteCategory(const auth::UserContext &user, long long categoryId);
|
||||
Json::Value deleteArea(const auth::UserContext &user, long long areaId);
|
||||
Json::Value deleteTeam(const auth::UserContext &user, long long teamId);
|
||||
Json::Value updateCategory(const auth::UserContext &user,
|
||||
long long categoryId,
|
||||
const Json::Value &body);
|
||||
|
||||
Reference in New Issue
Block a user