// Copyright (C) 2023 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
import { includes, isArray, isEmpty, join, map } from 'lodash-es';
import { get } from '@vueuse/core';
import { type WebMessage } from "@vnuge/vnlib.browser"
import { type AxiosRequestConfig } from 'axios';
import { type MaybeRef } from 'vue';
import type { PostMeta, ContentMeta, ContentApi, BlogEntity, BlogAdminContext } from "../types";
/**
* Configures a content api for a given content endpoint and channel
* @param contentUrl The content endpoint url
* @param channel The channel to get the content for
* @returns A content api object
*/
export const useContent = (context : BlogAdminContext, channel: MaybeRef): ContentApi => {
const axios = context.getAxios();
const getUrl = (): string => {
const url = context.getContentUrl();
//Return the url with the channel id query
return `${url}?channel=${get(channel)}`;
}
const getContentType = (file: File): string => {
if (isEmpty(file.type)) {
return 'application/octet-stream'
}
if (includes(file.type, 'javascript')) {
return 'application/javascript'
}
if (includes(file.type, 'json')) {
return 'application/json'
}
if (includes(file.type, 'xml')) {
return 'application/xml'
}
return file.type;
}
/**
* Gets the raw content item from the server and returns a string of the content
* @param cotentId The id of the content to get the raw value of
* @returns A promise that resolves to the raw content string
*/
const _getContent = async (cotentId: string): Promise => {
const url = getUrl();
const response = await axios.get(`${url}&id=${cotentId}`);
return await response.data;
}
const getPostContent = async (post: BlogEntity): Promise => {
return await _getContent(post.id);
}
const getAllItems = async (): Promise => {
const url = getUrl();
const response = await axios.get(url);
return response.data;
}
const deleteContent = async (content: ContentMeta | ContentMeta[]): Promise => {
const url = getUrl();
if(isArray(content)){
const ids = join(map(content, x => x.id));
//bulk delete by setting multiple ids
const { data } = await axios.delete>(`${url}&ids=${ids}`);
//Delete results returns a webmessage that contains the ids of the successfully deleted items
const deleted = data.getResultOrThrow();
if(deleted.length !== content.length){
throw { message: 'Some items failed to delete' }
}
}
else{
await axios.delete(`${url}&id=${content.id}`);
}
}
const uploadContent = async (file: File, name: string, config?:AxiosRequestConfig): Promise => {
const url = getUrl();
//Endpoint returns the new content meta for the uploaded content
const { data } = await axios.put>(url, file, {
...config,
headers: {
'Content-Type': getContentType(file),
//Set the content name header as the supplied content name
'X-Content-Name': name
}
});
return data.getResultOrThrow();
}
const updatePostContent = async (post: PostMeta, content: string): Promise => {
const url = getUrl();
const { data } = await axios.put>(`${url}&id=${post.id}`, content, {
headers: {
'Content-Type': 'text/html',
//Set the content name header as the post id
'X-Content-Name': `Content for post ${post.id}`
}
});
return data.getResultOrThrow();
}
const updateContent = async (content: ContentMeta, data: File, config?: AxiosRequestConfig): Promise => {
const url = getUrl();
const response = await axios.put(`${url}&id=${content.id}`, data, {
...config,
headers: {
'Content-Type': getContentType(data),
//Set the content name header as the supplied content name
'X-Content-Name': content.name
}
});
return response.data;
}
const updateContentName = async (content: ContentMeta, name: string): Promise => {
const url = getUrl();
//Create a new object with the same properties as the content meta, but with the new name
const ct = { ...content, name: name }
const { data } = await axios.patch>(url, ct);
return data.getResultOrThrow();
}
const getPublicUrl = async (content: ContentMeta): Promise => {
//Get the public url from the server
const response = await axios.get(`${getUrl()}&id=${content.id}&getlink=true`);
//Response is a web-message
if (response.data?.success !== true) {
throw { response }
}
return response.data.result;
}
const getContent = async (id: string): Promise => {
const index = await getAllItems();
return index.find(x => x.id === id);
}
const downloadContent = async (content: ContentMeta): Promise => {
const url = getUrl();
const { data } = await axios.get(`${url}&id=${content.id}`, { responseType: 'blob' });
return data;
}
return {
getPostContent,
getAllItems,
delete:deleteContent,
uploadContent,
updateContentName,
updatePostContent,
updateContent,
getPublicUrl,
getContent,
downloadContent,
};
}