mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
178 lines
5.4 KiB
JavaScript
178 lines
5.4 KiB
JavaScript
// services/chibisafe.service.js
|
||
//
|
||
// Wraps the Chibisafe REST API.
|
||
//
|
||
// Environment variables expected:
|
||
// CHIBISAFE_BASE_URL – e.g. https://cdn.yourdomain.com
|
||
// CHIBISAFE_API_KEY – your personal / service-account API key
|
||
// CHIBISAFE_ALBUM_AVATARS – album UUID for avatar images
|
||
// CHIBISAFE_ALBUM_VIDEOS – album UUID for videos
|
||
// CHIBISAFE_ALBUM_DOCUMENTS – album UUID for documents (pdf, docx, ppt, txt…)
|
||
// CHIBISAFE_ALBUM_THUMBNAILS – album UUID for video thumbnails
|
||
// CHIBISAFE_ALBUM_ARCHIVED – album UUID used as the "trash" / archived album
|
||
|
||
const FormData = require("form-data");
|
||
const axios = require("axios");
|
||
|
||
// ─── Config ───────────────────────────────────────────────────────────────────
|
||
|
||
const BASE_URL = (process.env.CHIBISAFE_BASE_URL || "").replace(/\/$/, "");
|
||
const API_KEY = process.env.CHIBISAFE_API_KEY || "";
|
||
|
||
const ALBUMS = {
|
||
avatars: process.env.CHIBISAFE_ALBUM_AVATARS || null,
|
||
videos: process.env.CHIBISAFE_ALBUM_VIDEOS || null,
|
||
documents: process.env.CHIBISAFE_ALBUM_DOCUMENTS || null,
|
||
thumbnails: process.env.CHIBISAFE_ALBUM_THUMBNAILS || null,
|
||
images: process.env.CHIBISAFE_ALBUM_IMAGES || null, // ← new
|
||
archived: process.env.CHIBISAFE_ALBUM_ARCHIVED || null,
|
||
};
|
||
|
||
// ─── Internal helpers ─────────────────────────────────────────────────────────
|
||
|
||
function resolveAlbumUuid(ownerType = "") {
|
||
switch (ownerType) {
|
||
case "avatar": return ALBUMS.avatars;
|
||
case "video": return ALBUMS.videos;
|
||
case "document": return ALBUMS.documents;
|
||
case "thumbnail": return ALBUMS.thumbnails;
|
||
case "image": return ALBUMS.images;
|
||
default: return null;
|
||
}
|
||
}
|
||
|
||
function baseHeaders(extra = {}) {
|
||
return {
|
||
"x-api-key": API_KEY,
|
||
...extra,
|
||
};
|
||
}
|
||
|
||
// ─── Axios instance ───────────────────────────────────────────────────────────
|
||
|
||
const chibi = axios.create({
|
||
baseURL: BASE_URL,
|
||
maxBodyLength: Infinity, // ← required for large file uploads
|
||
maxContentLength: Infinity,
|
||
});
|
||
|
||
/**
|
||
* Thin axios wrapper that throws a descriptive error on non-2xx.
|
||
*/
|
||
async function chibiRequest(path, { method = "GET", headers = {}, data } = {}) {
|
||
try {
|
||
const res = await chibi.request({
|
||
url: path,
|
||
method,
|
||
headers,
|
||
data,
|
||
});
|
||
return res.data;
|
||
} catch (err) {
|
||
const status = err.response?.status;
|
||
const body = err.response?.data ?? {};
|
||
const msg = body?.message || body?.error || err.message;
|
||
const friendly = new Error(`[Chibisafe] ${status ?? "?"} – ${msg}`);
|
||
friendly.status = status;
|
||
friendly.chibiBody = body;
|
||
throw friendly;
|
||
}
|
||
}
|
||
|
||
// ─── Public API ───────────────────────────────────────────────────────────────
|
||
|
||
/**
|
||
* Upload a file to Chibisafe, optionally straight into a typed album.
|
||
*/
|
||
async function uploadFile({ buffer, originalname, mimetype, ownerType = "" }) {
|
||
if (!BASE_URL || !API_KEY) {
|
||
throw new Error("[Chibisafe] CHIBISAFE_BASE_URL or CHIBISAFE_API_KEY is not configured.");
|
||
}
|
||
|
||
const albumUuid = resolveAlbumUuid(ownerType);
|
||
|
||
const form = new FormData();
|
||
form.append("file", buffer, {
|
||
filename: originalname,
|
||
contentType: mimetype,
|
||
});
|
||
|
||
const contentLength = await new Promise((resolve, reject) => {
|
||
form.getLength((err, length) => (err ? reject(err) : resolve(length)));
|
||
});
|
||
|
||
const data = await chibiRequest("/api/upload", {
|
||
method: "POST",
|
||
headers: {
|
||
...baseHeaders(),
|
||
...form.getHeaders(),
|
||
"Content-Length": contentLength,
|
||
...(albumUuid ? { albumuuid: albumUuid } : {}),
|
||
},
|
||
data: form,
|
||
});
|
||
|
||
return { uuid: data.uuid, url: data.url, name: data.name };
|
||
}
|
||
|
||
/**
|
||
* Permanently delete one file from Chibisafe by its UUID.
|
||
*/
|
||
async function deleteFile(uuid) {
|
||
await chibiRequest(`/api/file/${uuid}`, {
|
||
method: "DELETE",
|
||
headers: baseHeaders(),
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Move one or more files into the "archived" album.
|
||
*/
|
||
async function archiveFiles(uuids) {
|
||
if (!ALBUMS.archived) {
|
||
throw new Error("[Chibisafe] CHIBISAFE_ALBUM_ARCHIVED is not configured.");
|
||
}
|
||
|
||
const ids = Array.isArray(uuids) ? uuids : [uuids];
|
||
if (!ids.length) return;
|
||
|
||
await chibiRequest("/api/files/album/add", {
|
||
method: "POST",
|
||
headers: {
|
||
...baseHeaders(),
|
||
"Content-Type": "application/json",
|
||
},
|
||
data: {
|
||
files: ids,
|
||
albumUuid: ALBUMS.archived,
|
||
},
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Move one or more files into a specific album by UUID.
|
||
*/
|
||
async function addFilesToAlbum(uuids, albumUuid) {
|
||
const ids = Array.isArray(uuids) ? uuids : [uuids];
|
||
|
||
await chibiRequest("/api/files/album/add", {
|
||
method: "POST",
|
||
headers: {
|
||
...baseHeaders(),
|
||
"Content-Type": "application/json",
|
||
},
|
||
data: {
|
||
files: ids,
|
||
albumUuid,
|
||
},
|
||
});
|
||
}
|
||
|
||
module.exports = {
|
||
uploadFile,
|
||
deleteFile,
|
||
archiveFiles,
|
||
addFilesToAlbum,
|
||
ALBUMS,
|
||
resolveAlbumUuid,
|
||
}; |