mirror of
https://github.com/Arcadia-Solutions/arcadia.git
synced 2025-12-16 23:14:15 -06:00
@@ -103,6 +103,7 @@ use crate::handlers::user_applications::get_user_applications::GetUserApplicatio
|
||||
crate::handlers::staff_pms::get_staff_pm::exec,
|
||||
crate::handlers::staff_pms::list_staff_pms::exec,
|
||||
crate::handlers::staff_pms::resolve_staff_pm::exec,
|
||||
crate::handlers::staff_pms::unresolve_staff_pm::exec,
|
||||
crate::handlers::collages::create_collage::exec,
|
||||
crate::handlers::collages::create_collage_entries::exec,
|
||||
crate::handlers::collages::get_collage::exec,
|
||||
|
||||
@@ -3,6 +3,7 @@ pub mod create_staff_pm_message;
|
||||
pub mod get_staff_pm;
|
||||
pub mod list_staff_pms;
|
||||
pub mod resolve_staff_pm;
|
||||
pub mod unresolve_staff_pm;
|
||||
|
||||
use actix_web::web::{get, post, put, resource, ServiceConfig};
|
||||
use arcadia_storage::redis::RedisPoolInterface;
|
||||
@@ -16,4 +17,5 @@ pub fn config<R: RedisPoolInterface + 'static>(cfg: &mut ServiceConfig) {
|
||||
cfg.service(resource("/messages").route(post().to(self::create_staff_pm_message::exec::<R>)));
|
||||
cfg.service(resource("/{id}").route(get().to(self::get_staff_pm::exec::<R>)));
|
||||
cfg.service(resource("/{id}/resolve").route(put().to(self::resolve_staff_pm::exec::<R>)));
|
||||
cfg.service(resource("/{id}/unresolve").route(put().to(self::unresolve_staff_pm::exec::<R>)));
|
||||
}
|
||||
|
||||
31
backend/api/src/handlers/staff_pms/unresolve_staff_pm.rs
Normal file
31
backend/api/src/handlers/staff_pms/unresolve_staff_pm.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
use crate::{middlewares::auth_middleware::Authdata, Arcadia};
|
||||
use actix_web::{
|
||||
web::{Data, Path},
|
||||
HttpResponse,
|
||||
};
|
||||
use arcadia_common::error::{Error, Result};
|
||||
use arcadia_storage::{models::user::UserClass, redis::RedisPoolInterface};
|
||||
|
||||
#[utoipa::path(
|
||||
put,
|
||||
operation_id = "Unresolve staff PM",
|
||||
tag = "StaffPM",
|
||||
path = "/api/staff-pms/{id}/unresolve",
|
||||
params(("id" = i64, Path, description = "Staff PM id")),
|
||||
security(("http" = ["Bearer"])) ,
|
||||
responses((status = 200, description = "Unresolved staff PM", body = arcadia_storage::models::staff_pm::StaffPm))
|
||||
)]
|
||||
pub async fn exec<R: RedisPoolInterface + 'static>(
|
||||
arc: Data<Arcadia<R>>,
|
||||
user: Authdata,
|
||||
id: Path<i64>,
|
||||
) -> Result<HttpResponse> {
|
||||
if user.class != UserClass::Staff {
|
||||
return Err(Error::InsufficientPrivileges);
|
||||
}
|
||||
let updated = arc
|
||||
.pool
|
||||
.unresolve_staff_pm(id.into_inner(), user.sub)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok().json(updated))
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT title_group_id AS \"id!\", title_group_name AS \"name!\", title_group_covers AS \"covers!\",\n title_group_category AS \"category!: _\", title_group_content_type AS \"content_type!: _\", title_group_tag_names AS \"tags!\",\n title_group_original_release_date AS \"original_release_date!\", title_group_platform AS \"platform!: _\",\n '[]'::jsonb AS \"edition_groups!: _\",\n '[]'::jsonb AS \"affiliated_artists!: _\"\n\n FROM title_group_hierarchy_lite tgh\n\n WHERE ($4::BOOLEAN IS NULL OR tgh.torrent_staff_checked = $4)\n AND ($5::BOOLEAN IS NULL OR tgh.torrent_reported = $5)\n AND (\n $7::INT IS NULL OR\n -- don't return torrents created as anonymous\n -- unless the requesting user is the uploader\n (tgh.torrent_created_by_id = $7 AND (\n tgh.torrent_created_by_id = $8 OR\n NOT tgh.torrent_uploaded_as_anonymous)\n )\n )\n AND (\n $9::BIGINT IS NULL OR\n EXISTS (SELECT 1 FROM affiliated_artists aa WHERE aa.title_group_id = tgh.title_group_id AND aa.artist_id = $9)\n )\n -- name filter (partial match) or external link match or series name match\n AND (\n $10::TEXT IS NULL OR\n tgh.title_group_name ILIKE '%' || $10 || '%' ESCAPE '\\' OR\n tgh.title_group_series_name ILIKE '%' || $10 || '%' ESCAPE '\\'\n )\n AND ($11::TEXT IS NULL OR $11 = ANY(tgh.title_group_external_links))\n AND ($12::BOOLEAN IS TRUE OR tgh.torrent_id IS NOT NULL)\n\n GROUP BY title_group_id, title_group_name, title_group_covers, title_group_category,\n title_group_content_type, title_group_tag_names, title_group_original_release_date, title_group_platform\n\n ORDER BY\n CASE WHEN $1 = 'title_group_original_release_date' AND $6 = 'asc' THEN title_group_original_release_date END ASC,\n CASE WHEN $1 = 'title_group_original_release_date' AND $6 = 'desc' THEN title_group_original_release_date END DESC,\n CASE WHEN $1 = 'torrent_size' AND $6 = 'asc' THEN MAX(torrent_size) END ASC,\n CASE WHEN $1 = 'torrent_size' AND $6 = 'desc' THEN MAX(torrent_size) END DESC,\n CASE WHEN $1 = 'torrent_created_at' AND $6 = 'asc' THEN MAX(torrent_created_at) END ASC,\n CASE WHEN $1 = 'torrent_created_at' AND $6 = 'desc' THEN MAX(torrent_created_at) END DESC,\n title_group_original_release_date ASC\n\n LIMIT $2 OFFSET $3\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id!",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "name!",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "covers!",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "category!: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "title_group_category_enum",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"Ep",
|
||||
"Album",
|
||||
"Single",
|
||||
"Soundtrack",
|
||||
"Anthology",
|
||||
"Compilation",
|
||||
"Remix",
|
||||
"Bootleg",
|
||||
"Mixtape",
|
||||
"ConcertRecording",
|
||||
"DjMix",
|
||||
"FeatureFilm",
|
||||
"ShortFilm",
|
||||
"Game",
|
||||
"Program",
|
||||
"Illustrated",
|
||||
"Periodical",
|
||||
"Book",
|
||||
"Article",
|
||||
"Manual",
|
||||
"Other"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "content_type!: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "content_type_enum",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"movie",
|
||||
"video",
|
||||
"tv_show",
|
||||
"music",
|
||||
"podcast",
|
||||
"software",
|
||||
"book",
|
||||
"collection"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "tags!",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "original_release_date!",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "platform!: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "platform_enum",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"Linux",
|
||||
"MacOS",
|
||||
"Windows",
|
||||
"Xbox"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "edition_groups!: _",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "affiliated_artists!: _",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Int8",
|
||||
"Int8",
|
||||
"Bool",
|
||||
"Bool",
|
||||
"Text",
|
||||
"Int4",
|
||||
"Int4",
|
||||
"Int8",
|
||||
"Text",
|
||||
"Text",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "3a104da12b9d05a2af0eff0085dbbecdee013c49ba2985d63910b036831b718e"
|
||||
}
|
||||
46
backend/storage/.sqlx/query-f4438a4333a4291efeb8634199872c0a4679483ba059d07bc6cfbc61389eac36.json
generated
Normal file
46
backend/storage/.sqlx/query-f4438a4333a4291efeb8634199872c0a4679483ba059d07bc6cfbc61389eac36.json
generated
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n\t\t\t\tUPDATE staff_pms\n\t\t\t\tSET resolved = FALSE\n\t\t\t\tWHERE id = $1\n\t\t\t\tRETURNING *\n\t\t\t",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "subject",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "created_by_id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "resolved",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "f4438a4333a4291efeb8634199872c0a4679483ba059d07bc6cfbc61389eac36"
|
||||
}
|
||||
@@ -78,6 +78,28 @@ impl ConnectionPool {
|
||||
Ok(updated)
|
||||
}
|
||||
|
||||
pub async fn unresolve_staff_pm(
|
||||
&self,
|
||||
staff_pm_id: i64,
|
||||
_current_user_id: i32,
|
||||
) -> Result<StaffPm> {
|
||||
let updated = sqlx::query_as!(
|
||||
StaffPm,
|
||||
r#"
|
||||
UPDATE staff_pms
|
||||
SET resolved = FALSE
|
||||
WHERE id = $1
|
||||
RETURNING *
|
||||
"#,
|
||||
staff_pm_id,
|
||||
)
|
||||
.fetch_one(self.borrow())
|
||||
.await
|
||||
.map_err(Error::CouldNotCreateConversation)?;
|
||||
|
||||
Ok(updated)
|
||||
}
|
||||
|
||||
pub async fn list_staff_pms(&self, current_user_id: i32, is_staff: bool) -> Result<Value> {
|
||||
let row = sqlx::query_unchecked!(
|
||||
r#"
|
||||
|
||||
@@ -356,6 +356,8 @@
|
||||
"resolve": "Resolve",
|
||||
"resolved": "Resolved",
|
||||
"resolved_successfully": "Staff PM resolved!",
|
||||
"unresolve": "Unresolve",
|
||||
"unresolved_successfully": "Staff PM unresolved!",
|
||||
"new": "New staff PM",
|
||||
"subject": "Subject",
|
||||
"message": "Message | Messages"
|
||||
|
||||
@@ -6631,6 +6631,43 @@ export const StaffPMApiAxiosParamCreator = function (configuration?: Configurati
|
||||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {number} id Staff PM id
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
unresolveStaffPM: async (id: number, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'id' is not null or undefined
|
||||
assertParamExists('unresolveStaffPM', 'id', id)
|
||||
const localVarPath = `/api/staff-pms/{id}/unresolve`
|
||||
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication http required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
@@ -6708,6 +6745,18 @@ export const StaffPMApiFp = function(configuration?: Configuration) {
|
||||
const localVarOperationServerBasePath = operationServerMap['StaffPMApi.resolveStaffPM']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {number} id Staff PM id
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async unresolveStaffPM(id: number, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<StaffPm>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.unresolveStaffPM(id, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['StaffPMApi.unresolveStaffPM']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6761,6 +6810,15 @@ export const StaffPMApiFactory = function (configuration?: Configuration, basePa
|
||||
resolveStaffPM(id: number, options?: RawAxiosRequestConfig): AxiosPromise<StaffPm> {
|
||||
return localVarFp.resolveStaffPM(id, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {number} id Staff PM id
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
unresolveStaffPM(id: number, options?: RawAxiosRequestConfig): AxiosPromise<StaffPm> {
|
||||
return localVarFp.unresolveStaffPM(id, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -6816,6 +6874,16 @@ export class StaffPMApi extends BaseAPI {
|
||||
public resolveStaffPM(id: number, options?: RawAxiosRequestConfig) {
|
||||
return StaffPMApiFp(this.configuration).resolveStaffPM(id, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} id Staff PM id
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
public unresolveStaffPM(id: number, options?: RawAxiosRequestConfig) {
|
||||
return StaffPMApiFp(this.configuration).unresolveStaffPM(id, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6853,6 +6921,12 @@ export const resolveStaffPM = async (id: number, options?: RawAxiosRequestConfig
|
||||
};
|
||||
|
||||
|
||||
export const unresolveStaffPM = async (id: number, options?: RawAxiosRequestConfig): Promise<StaffPm> => {
|
||||
const response = await staffPMApi.unresolveStaffPM(id, options);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* SubscriptionApi - axios parameter creator
|
||||
*/
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
>
|
||||
<template #buttons>
|
||||
<Button v-if="!staffPm.resolved" :label="t('staff_pm.resolve')" icon="pi pi-check" :loading="resolvingPm" @click="resolvePm" />
|
||||
<Button v-else :label="t('staff_pm.unresolve')" icon="pi pi-replay" :loading="unresolvingPm" @click="unresolvePm" />
|
||||
<Button type="submit" :label="t('general.send')" icon="pi pi-send" :loading="sendingMessage" />
|
||||
</template>
|
||||
</BBCodeEditor>
|
||||
@@ -29,7 +30,14 @@ import { Button } from 'primevue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { showToast } from '@/main'
|
||||
import { createStaffPMMessage, getStaffPM, resolveStaffPM, type StaffPmHierarchy, type UserCreatedStaffPmMessage } from '@/services/api-schema'
|
||||
import {
|
||||
createStaffPMMessage,
|
||||
getStaffPM,
|
||||
resolveStaffPM,
|
||||
unresolveStaffPM,
|
||||
type StaffPmHierarchy,
|
||||
type UserCreatedStaffPmMessage,
|
||||
} from '@/services/api-schema'
|
||||
|
||||
const route = useRoute()
|
||||
const { t } = useI18n()
|
||||
@@ -38,6 +46,7 @@ const userStore = useUserStore()
|
||||
const staffPm = ref<StaffPmHierarchy>()
|
||||
const sendingMessage = ref(false)
|
||||
const resolvingPm = ref(false)
|
||||
const unresolvingPm = ref(false)
|
||||
const newMessage = ref<UserCreatedStaffPmMessage>({
|
||||
content: '',
|
||||
staff_pm_id: 0,
|
||||
@@ -66,6 +75,20 @@ const resolvePm = async () => {
|
||||
})
|
||||
}
|
||||
|
||||
const unresolvePm = async () => {
|
||||
unresolvingPm.value = true
|
||||
unresolveStaffPM(parseInt(route.params.id as string))
|
||||
.then(() => {
|
||||
if (staffPm.value) {
|
||||
staffPm.value.resolved = false
|
||||
}
|
||||
showToast('', t('staff_pm.unresolved_successfully'), 'success', 3000, true, 'tr')
|
||||
})
|
||||
.finally(() => {
|
||||
unresolvingPm.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const sendMessage = async () => {
|
||||
sendingMessage.value = true
|
||||
newMessage.value.staff_pm_id = parseInt(route.params.id as string)
|
||||
|
||||
Reference in New Issue
Block a user