mirror of
https://github.com/yusufipk/OpenFrame.git
synced 2026-09-11 09:36:08 +00:00
refactor: eslint and prettier conflict will be resolved and formatted
This commit is contained in:
@@ -21,7 +21,8 @@ type BunnyVideo = {
|
||||
|
||||
function getBunnyConfig(): BunnyConfig {
|
||||
const apiKey = process.env.BUNNY_STREAM_API_KEY;
|
||||
const libraryId = process.env.BUNNY_STREAM_LIBRARY_ID || process.env.NEXT_PUBLIC_BUNNY_STREAM_LIBRARY_ID;
|
||||
const libraryId =
|
||||
process.env.BUNNY_STREAM_LIBRARY_ID || process.env.NEXT_PUBLIC_BUNNY_STREAM_LIBRARY_ID;
|
||||
|
||||
if (!apiKey || !libraryId) {
|
||||
throw new Error('Missing BUNNY_STREAM_API_KEY or BUNNY_STREAM_LIBRARY_ID.');
|
||||
@@ -72,7 +73,10 @@ function parseUploadedAt(item: unknown): Date | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
async function fetchBunnyPage(config: BunnyConfig, page: number): Promise<{ items: unknown[]; totalItems: number | null }> {
|
||||
async function fetchBunnyPage(
|
||||
config: BunnyConfig,
|
||||
page: number
|
||||
): Promise<{ items: unknown[]; totalItems: number | null }> {
|
||||
const response = await fetch(
|
||||
`${BUNNY_API_BASE}/library/${config.libraryId}/videos?page=${page}&itemsPerPage=${ITEMS_PER_PAGE}`,
|
||||
{
|
||||
@@ -95,16 +99,23 @@ async function fetchBunnyPage(config: BunnyConfig, page: number): Promise<{ item
|
||||
|
||||
const items = Array.isArray(record.items)
|
||||
? record.items
|
||||
: (Array.isArray(record.Items) ? record.Items : []);
|
||||
: Array.isArray(record.Items)
|
||||
? record.Items
|
||||
: [];
|
||||
|
||||
const totalItems = typeof record.totalItems === 'number'
|
||||
? record.totalItems
|
||||
: (typeof record.TotalItems === 'number' ? record.TotalItems : null);
|
||||
const totalItems =
|
||||
typeof record.totalItems === 'number'
|
||||
? record.totalItems
|
||||
: typeof record.TotalItems === 'number'
|
||||
? record.TotalItems
|
||||
: null;
|
||||
|
||||
return { items, totalItems };
|
||||
}
|
||||
|
||||
async function listBunnyVideos(config: BunnyConfig): Promise<{ videos: BunnyVideo[]; scanned: number; skippedInvalid: number }> {
|
||||
async function listBunnyVideos(
|
||||
config: BunnyConfig
|
||||
): Promise<{ videos: BunnyVideo[]; scanned: number; skippedInvalid: number }> {
|
||||
const videos: BunnyVideo[] = [];
|
||||
let scanned = 0;
|
||||
let skippedInvalid = 0;
|
||||
@@ -171,7 +182,10 @@ async function findReferencedVideoIds(videoIds: string[]): Promise<Set<string>>
|
||||
return referenced;
|
||||
}
|
||||
|
||||
async function deleteBunnyVideo(config: BunnyConfig, videoId: string): Promise<'deleted' | 'already_missing'> {
|
||||
async function deleteBunnyVideo(
|
||||
config: BunnyConfig,
|
||||
videoId: string
|
||||
): Promise<'deleted' | 'already_missing'> {
|
||||
const response = await fetch(
|
||||
`${BUNNY_API_BASE}/library/${config.libraryId}/videos/${encodeURIComponent(videoId)}`,
|
||||
{
|
||||
@@ -186,7 +200,9 @@ async function deleteBunnyVideo(config: BunnyConfig, videoId: string): Promise<'
|
||||
if (response.ok) return 'deleted';
|
||||
|
||||
const body = await response.text().catch(() => '');
|
||||
throw new Error(`Bunny delete API failed for ${videoId} (${response.status}): ${body.slice(0, 300)}`);
|
||||
throw new Error(
|
||||
`Bunny delete API failed for ${videoId} (${response.status}): ${body.slice(0, 300)}`
|
||||
);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
@@ -200,8 +216,12 @@ async function main() {
|
||||
console.log(`[bunny-orphan-cleanup] Grace period: ${graceHours}h`);
|
||||
|
||||
const expiredBillingCleanup = await cleanupExpiredBillingWorkspaces({ dryRun });
|
||||
console.log(`[bunny-orphan-cleanup] Expired owner workspaces scanned: ${expiredBillingCleanup.scanned}`);
|
||||
console.log(`[bunny-orphan-cleanup] Expired owner workspaces deleted: ${expiredBillingCleanup.deleted}`);
|
||||
console.log(
|
||||
`[bunny-orphan-cleanup] Expired owner workspaces scanned: ${expiredBillingCleanup.scanned}`
|
||||
);
|
||||
console.log(
|
||||
`[bunny-orphan-cleanup] Expired owner workspaces deleted: ${expiredBillingCleanup.deleted}`
|
||||
);
|
||||
|
||||
const { videos, scanned, skippedInvalid } = await listBunnyVideos(config);
|
||||
const eligible = videos.filter((video) => video.uploadedAt.getTime() <= cutoff);
|
||||
|
||||
@@ -104,11 +104,15 @@ async function main() {
|
||||
const failedMigrations = migrationRows.filter((row) => !row.finished_at && !row.rolled_back_at);
|
||||
const shouldBootstrapFreshSchema = !hasCoreTables;
|
||||
|
||||
console.log(`Detected public tables: ${appTables.length > 0 ? appTables.join(', ') : '(none)'}`);
|
||||
console.log(
|
||||
`Detected public tables: ${appTables.length > 0 ? appTables.join(', ') : '(none)'}`
|
||||
);
|
||||
|
||||
if (shouldBootstrapFreshSchema) {
|
||||
if (failedMigrations.length > 0) {
|
||||
console.log('Detected failed migration state on a fresh database. Marking failed migrations as rolled back.');
|
||||
console.log(
|
||||
'Detected failed migration state on a fresh database. Marking failed migrations as rolled back.'
|
||||
);
|
||||
for (const migration of failedMigrations) {
|
||||
await runPrisma(['migrate', 'resolve', '--rolled-back', migration.migration_name]);
|
||||
}
|
||||
|
||||
@@ -19,26 +19,28 @@ async function getExpiredWorkspaceTargets(): Promise<ExpiredWorkspaceTarget[]> {
|
||||
return [];
|
||||
}
|
||||
|
||||
return db.workspace.findMany({
|
||||
where: {
|
||||
ownerId: { in: expiredOwners.map((owner) => owner.id) },
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
ownerId: true,
|
||||
owner: {
|
||||
select: {
|
||||
email: true,
|
||||
return db.workspace
|
||||
.findMany({
|
||||
where: {
|
||||
ownerId: { in: expiredOwners.map((owner) => owner.id) },
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
ownerId: true,
|
||||
owner: {
|
||||
select: {
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}).then((workspaces) =>
|
||||
workspaces.map((workspace) => ({
|
||||
id: workspace.id,
|
||||
ownerId: workspace.ownerId,
|
||||
ownerEmail: workspace.owner.email,
|
||||
}))
|
||||
);
|
||||
})
|
||||
.then((workspaces) =>
|
||||
workspaces.map((workspace) => ({
|
||||
id: workspace.id,
|
||||
ownerId: workspace.ownerId,
|
||||
ownerEmail: workspace.owner.email,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
export async function cleanupExpiredBillingWorkspaces(options?: { dryRun?: boolean }) {
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { DeleteObjectCommand, ListObjectsV2Command, type ListObjectsV2CommandInput } from '@aws-sdk/client-s3';
|
||||
import {
|
||||
DeleteObjectCommand,
|
||||
ListObjectsV2Command,
|
||||
type ListObjectsV2CommandInput,
|
||||
} from '@aws-sdk/client-s3';
|
||||
import { db, disconnectDb } from '../lib/db';
|
||||
import { r2Client, R2_BUCKET_NAME } from '../lib/r2';
|
||||
import { cleanupExpiredBillingWorkspaces } from './expired-billing-cleanup';
|
||||
@@ -14,7 +18,10 @@ type CleanupCandidate = {
|
||||
};
|
||||
|
||||
type UserFeedbackScreenshotDelegate = {
|
||||
findMany: (args: { where: { url: { in: string[] } }; select: { url: true } }) => Promise<Array<{ url: string }>>;
|
||||
findMany: (args: {
|
||||
where: { url: { in: string[] } };
|
||||
select: { url: true };
|
||||
}) => Promise<Array<{ url: string }>>;
|
||||
};
|
||||
|
||||
function keyToProxyUrl(key: string): string | null {
|
||||
@@ -37,7 +44,10 @@ function chunk<T>(items: T[], size: number): T[][] {
|
||||
return out;
|
||||
}
|
||||
|
||||
async function listCleanupCandidates(): Promise<{ candidates: CleanupCandidate[]; scanned: number }> {
|
||||
async function listCleanupCandidates(): Promise<{
|
||||
candidates: CleanupCandidate[];
|
||||
scanned: number;
|
||||
}> {
|
||||
const candidates: CleanupCandidate[] = [];
|
||||
const cutoff = Date.now() - UNATTACHED_UPLOAD_TTL_MS;
|
||||
let scanned = 0;
|
||||
@@ -76,9 +86,11 @@ async function listCleanupCandidates(): Promise<{ candidates: CleanupCandidate[]
|
||||
|
||||
async function findReferencedUrls(urls: string[]): Promise<Set<string>> {
|
||||
const referenced = new Set<string>();
|
||||
const userFeedbackScreenshotDelegate = (db as unknown as {
|
||||
userFeedbackScreenshot?: UserFeedbackScreenshotDelegate;
|
||||
}).userFeedbackScreenshot;
|
||||
const userFeedbackScreenshotDelegate = (
|
||||
db as unknown as {
|
||||
userFeedbackScreenshot?: UserFeedbackScreenshotDelegate;
|
||||
}
|
||||
).userFeedbackScreenshot;
|
||||
|
||||
for (const group of chunk(urls, CHUNK_SIZE)) {
|
||||
const [commentRows, feedbackRows, feedbackAttachmentRows, assetRows] = await Promise.all([
|
||||
@@ -97,9 +109,9 @@ async function findReferencedUrls(urls: string[]): Promise<Set<string>> {
|
||||
}),
|
||||
userFeedbackScreenshotDelegate
|
||||
? userFeedbackScreenshotDelegate.findMany({
|
||||
where: { url: { in: group } },
|
||||
select: { url: true },
|
||||
})
|
||||
where: { url: { in: group } },
|
||||
select: { url: true },
|
||||
})
|
||||
: Promise.resolve([] as Array<{ url: string }>),
|
||||
db.videoAsset.findMany({
|
||||
where: { sourceUrl: { in: group } },
|
||||
@@ -130,11 +142,17 @@ async function main() {
|
||||
console.log(`[r2-orphan-cleanup] Starting (${dryRun ? 'dry-run' : 'delete mode'})`);
|
||||
|
||||
const expiredBillingCleanup = await cleanupExpiredBillingWorkspaces({ dryRun });
|
||||
console.log(`[r2-orphan-cleanup] Expired owner workspaces scanned: ${expiredBillingCleanup.scanned}`);
|
||||
console.log(`[r2-orphan-cleanup] Expired owner workspaces deleted: ${expiredBillingCleanup.deleted}`);
|
||||
console.log(
|
||||
`[r2-orphan-cleanup] Expired owner workspaces scanned: ${expiredBillingCleanup.scanned}`
|
||||
);
|
||||
console.log(
|
||||
`[r2-orphan-cleanup] Expired owner workspaces deleted: ${expiredBillingCleanup.deleted}`
|
||||
);
|
||||
|
||||
const { candidates, scanned } = await listCleanupCandidates();
|
||||
console.log(`[r2-orphan-cleanup] Scanned: ${scanned}, eligible (old enough): ${candidates.length}`);
|
||||
console.log(
|
||||
`[r2-orphan-cleanup] Scanned: ${scanned}, eligible (old enough): ${candidates.length}`
|
||||
);
|
||||
|
||||
if (candidates.length === 0) {
|
||||
console.log('[r2-orphan-cleanup] No eligible objects found');
|
||||
|
||||
@@ -2,7 +2,9 @@ import 'dotenv/config';
|
||||
import { ensureR2BucketExists, R2_BUCKET_NAME } from '@/lib/r2';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const shouldCreateBucket = /^(1|true|yes|on)$/i.test(process.env.SELF_HOSTED_AUTO_CREATE_BUCKET ?? '');
|
||||
const shouldCreateBucket = /^(1|true|yes|on)$/i.test(
|
||||
process.env.SELF_HOSTED_AUTO_CREATE_BUCKET ?? ''
|
||||
);
|
||||
|
||||
async function main() {
|
||||
if (!shouldCreateBucket) {
|
||||
|
||||
Reference in New Issue
Block a user