Add support for Lottie stickers and enhance disk space API endpoint
- Introduced Lottie sticker component with placeholder handling in DiscordLogs. - Expanded Discord message types in DiscordLogs component. - Implemented disk space fetching in MediaRequestForm with visual indicator. - Enhanced API for fetching Discord messages to include Lottie data for stickers. - Added disk space API endpoint with authentication and authorization checks.
This commit is contained in:
@@ -604,14 +604,15 @@ export async function GET({ request }) {
|
||||
GROUP BY r.message_id, r.emoji_id, r.emoji_name, r.emoji_animated, e.cached_image_id
|
||||
`;
|
||||
|
||||
// Fetch stickers for all messages (include cached_image_id for locally cached stickers)
|
||||
// Fetch stickers for all messages (include cached_image_id and lottie_data for locally cached stickers)
|
||||
const stickers = await sql`
|
||||
SELECT
|
||||
ms.message_id,
|
||||
s.sticker_id,
|
||||
s.name,
|
||||
s.format_type,
|
||||
s.cached_image_id
|
||||
s.cached_image_id,
|
||||
s.lottie_data
|
||||
FROM message_stickers ms
|
||||
JOIN stickers s ON ms.sticker_id = s.sticker_id
|
||||
WHERE ms.message_id = ANY(${messageIds})
|
||||
@@ -910,18 +911,19 @@ export async function GET({ request }) {
|
||||
}
|
||||
|
||||
// Sticker format types: 1=PNG, 2=APNG, 3=Lottie, 4=GIF
|
||||
const stickerExtensions = { 1: 'png', 2: 'png', 3: 'json', 4: 'gif' };
|
||||
const stickerExtensions = { 1: 'png', 2: 'png', 3: 'png', 4: 'gif' };
|
||||
for (const sticker of stickers) {
|
||||
if (!stickersByMessage[sticker.message_id]) {
|
||||
stickersByMessage[sticker.message_id] = [];
|
||||
}
|
||||
const ext = stickerExtensions[sticker.format_type] || 'png';
|
||||
// Use cached sticker image if available, otherwise fall back to Discord CDN
|
||||
let stickerUrl;
|
||||
let stickerUrl = null;
|
||||
if (sticker.cached_image_id) {
|
||||
const sig = signImageId(sticker.cached_image_id);
|
||||
stickerUrl = `${baseUrl}/api/discord/cached-image?id=${sticker.cached_image_id}&sig=${sig}`;
|
||||
} else {
|
||||
} else if (sticker.format_type !== 3) {
|
||||
// Only use CDN fallback for non-Lottie stickers (Lottie will use lottie_data)
|
||||
stickerUrl = `https://media.discordapp.net/stickers/${sticker.sticker_id}.${ext}?size=160`;
|
||||
}
|
||||
stickersByMessage[sticker.message_id].push({
|
||||
@@ -929,6 +931,7 @@ export async function GET({ request }) {
|
||||
name: sticker.name,
|
||||
formatType: sticker.format_type,
|
||||
url: stickerUrl,
|
||||
lottieData: sticker.lottie_data || null,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
70
src/pages/api/disk-space.js
Normal file
70
src/pages/api/disk-space.js
Normal file
@@ -0,0 +1,70 @@
|
||||
import { exec } from 'child_process';
|
||||
import { promisify } from 'util';
|
||||
import { requireApiAuth } from '../../utils/apiAuth.js';
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
export async function GET({ request }) {
|
||||
// Check authentication
|
||||
const { user, error: authError, setCookieHeader } = await requireApiAuth(request);
|
||||
if (authError) return authError;
|
||||
|
||||
// Check authorization - must have 'admin' or 'trip' role
|
||||
const userRoles = user?.roles || [];
|
||||
const hasAccess = userRoles.includes('admin') || userRoles.includes('trip');
|
||||
|
||||
if (!hasAccess) {
|
||||
return new Response(JSON.stringify({
|
||||
error: 'Forbidden',
|
||||
message: 'Insufficient permissions. Requires admin or trip role.'
|
||||
}), {
|
||||
status: 403,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Get disk space for root filesystem
|
||||
const { stdout } = await execAsync("df -B1 / | tail -1 | awk '{print $2,$3,$4}'");
|
||||
const [total, used, available] = stdout.trim().split(/\s+/).map(Number);
|
||||
|
||||
if (!total || !available) {
|
||||
throw new Error('Failed to parse disk space');
|
||||
}
|
||||
|
||||
const usedPercent = Math.round((used / total) * 100);
|
||||
|
||||
const responseHeaders = { 'Content-Type': 'application/json' };
|
||||
if (setCookieHeader) {
|
||||
responseHeaders['Set-Cookie'] = setCookieHeader;
|
||||
}
|
||||
|
||||
return new Response(JSON.stringify({
|
||||
total,
|
||||
used,
|
||||
available,
|
||||
usedPercent,
|
||||
// Human-readable versions
|
||||
totalFormatted: formatBytes(total),
|
||||
usedFormatted: formatBytes(used),
|
||||
availableFormatted: formatBytes(available),
|
||||
}), {
|
||||
status: 200,
|
||||
headers: responseHeaders,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Error getting disk space:', err);
|
||||
return new Response(JSON.stringify({ error: 'Failed to get disk space' }), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function formatBytes(bytes) {
|
||||
if (bytes === 0) return '0 B';
|
||||
const k = 1024;
|
||||
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||
}
|
||||
Reference in New Issue
Block a user