Commit ef4c305c authored by duanjinfei's avatar duanjinfei

clean up storage

parent 4e1d8e24
// Follow this setup guide to integrate the Deno language server with your editor:
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts"
import { createClient } from 'jsr:@supabase/supabase-js@2'
// 删除目录和其下的文件
async function deleteDirectory(bucket: string, directory: string) {
const { data, error } = await supabase.storage.from(bucket).list(directory, {
limit: 1000, // 批量获取文件
});
if (error) {
console.error(`Failed to list files in directory "${directory}":`, error.message);
return;
}
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { createClient } from "jsr:@supabase/supabase-js@2";
const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "authorization, content-type",
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
};
if (data && data.length > 0) {
const pathsToDelete = data.map((file) => `${directory}/${file.name}`);
const { error: deleteError } = await supabase.storage.from(bucket).remove(pathsToDelete);
// 删除指定目录及其下的文件
async function deleteDirectory(supabase, bucket: string, directory: string) {
try {
const { data, error } = await supabase.storage.from(bucket).list(directory, {
limit: 1000,
});
if (deleteError) {
console.error(`Failed to delete files in directory "${directory}":`, deleteError.message);
} else {
console.log(`Successfully deleted directory "${directory}" and its contents.`);
if (error) {
console.error(`Failed to list files in directory "${directory}":`, error.message);
return;
}
if (data && data.length > 0) {
const pathsToDelete = data.map((file) => `${directory}/${file.name}`);
const { error: deleteError } = await supabase.storage.from(bucket).remove(pathsToDelete);
if (deleteError) {
console.error(`Failed to delete files in directory "${directory}":`, deleteError.message);
} else {
console.log(`Successfully deleted directory "${directory}" and its contents.`);
}
}
} catch (err) {
console.error(`Error deleting directory "${directory}":`, err);
}
}
// 获取需要删除的时间戳目录
function getOldDirectories(currentTimestamp: string, interval: "hour" | "day", keepCount: number) {
const now = new Date(currentTimestamp);
// 获取当前整点时间
function getCurrentHourTimestamp(): Date {
const now = new Date();
return new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours());
}
const oldTimestamps = [];
for (let i = keepCount; i < keepCount + 7; i++) {
const oldDate = new Date(now);
// 获取东八区当天凌晨3点时间
function getTodayThreeAMTimestamp(): Date {
const now = new Date();
const today3AM = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 3, 0, 0);
if (interval === "hour") {
oldDate.setHours(now.getHours() - i);
} else if (interval === "day") {
oldDate.setDate(now.getDate() - i);
}
// 如果当前时间小于凌晨3点,返回前一天的凌晨3点
if (now.getHours() < 3) {
today3AM.setDate(today3AM.getDate() - 1);
}
oldTimestamps.push(oldDate.toISOString().split(":")[0]); // 精确到小时,避免分钟秒
return today3AM;
}
// 计算截止时间(7个周期之前的时间戳)
function getCutoffTimestamp(type: "app-category" | "user-rank"): number {
if (type === "app-category") {
const currentHour = getCurrentHourTimestamp();
const cutoff = new Date(currentHour);
cutoff.setHours(cutoff.getHours() - 7);
return cutoff.getTime();
} else {
const today3AM = getTodayThreeAMTimestamp();
const cutoff = new Date(today3AM);
cutoff.setDate(cutoff.getDate() - 7);
return cutoff.getTime();
}
}
// 获取需要删除的目录
async function getDirectoriesToDelete(supabase, bucket: string, type: "app-category" | "user-rank"): Promise<string[]> {
try {
const { data, error } = await supabase.storage.from(bucket).list(`${type}/`, {
limit: 1000,
});
if (error) {
console.error(`Failed to list directories in ${type}:`, error.message);
return [];
}
const cutoffTimestamp = getCutoffTimestamp(type);
return oldTimestamps;
const dirsToDelete = data
.filter(item => !item.name.includes('.')) // 只处理目录
.filter(item => {
const dirTimestamp = parseInt(item.name); // 转为秒
if (isNaN(dirTimestamp)) {
console.log(`Invalid timestamp directory: ${item.name}`);
return false;
}
const dirDate = new Date(dirTimestamp * 1000); // 转为日期
const shouldDelete = dirTimestamp * 1000 < cutoffTimestamp; // 确保单位统一为毫秒
console.log(`Directory "${item.name}" date: ${dirDate.toISOString()}, should delete: ${shouldDelete}`);
return shouldDelete;
})
.map(item => item.name);
console.log(`Found ${dirsToDelete.length} directories to delete for ${type}`);
return dirsToDelete;
} catch (err) {
console.error(`Error listing directories in ${type}:`, err);
return [];
}
}
// 主逻辑处理
Deno.serve(async (req) => {
if (req.method === 'OPTIONS') {
return new Response('ok', { headers: corsHeaders })
if (req.method === "OPTIONS") {
return new Response("ok", { headers: corsHeaders });
}
try {
// const { name } = await req.json()
const supabase = createClient(
Deno.env.get('SUPABASE_URL') ?? '',
Deno.env.get('SUPABASE_ANON_KEY') ?? '',
{ global: { headers: { Authorization: req.headers.get('Authorization')! } } }
)
const currentTimestamp = new Date().toISOString();
// 处理 app-category:按小时保留前 7 个周期
const oldAppCategoryDirs = getOldDirectories(currentTimestamp, "hour", 7);
for (const dir of oldAppCategoryDirs) {
await deleteDirectory("cache", `app-category/${dir}`);
const supabaseUrl = Deno.env.get("SUPABASE_URL");
const supabaseAnonKey = Deno.env.get("SUPABASE_ANON_KEY");
const authHeader = req.headers.get("Authorization");
if (!supabaseUrl || !supabaseAnonKey || !authHeader) {
throw new Error("Missing Supabase configuration or Authorization header.");
}
const supabase = createClient(supabaseUrl, supabaseAnonKey, {
global: { headers: { Authorization: authHeader } },
});
console.log("Starting cleanup process...");
// 处理 app-category(每小时)
const appCategoryDirsToDelete = await getDirectoriesToDelete(supabase, "cache", "app-category");
for (const dir of appCategoryDirsToDelete) {
await deleteDirectory(supabase, "cache", `app-category/${dir}`);
}
// 处理 user-rank:按天保留前 7 个周期
const oldUserRankDirs = getOldDirectories(currentTimestamp, "day", 7);
for (const dir of oldUserRankDirs) {
await deleteDirectory("cache", `user-rank/${dir}`);
// 处理 user-rank(每天凌晨3点)
const userRankDirsToDelete = await getDirectoriesToDelete(supabase, "cache", "user-rank");
for (const dir of userRankDirsToDelete) {
await deleteDirectory(supabase, "cache", `user-rank/${dir}`);
}
return new Response(JSON.stringify({
code: 200,
data: null,
message: 'success'
}), {
headers: { 'Content-Type': 'application/json' },
status: 200,
})
return new Response(
JSON.stringify({
code: 200,
data: {
appCategory: {
deletedDirs: appCategoryDirsToDelete,
cutoffTime: new Date(getCutoffTimestamp("app-category")).toISOString(),
},
userRank: {
deletedDirs: userRankDirsToDelete,
cutoffTime: new Date(getCutoffTimestamp("user-rank")).toISOString(),
},
},
message: "success",
}),
{ headers: { ...corsHeaders, "Content-Type": "application/json" }, status: 200 }
);
} catch (err) {
return new Response(String(err?.message ?? err), { status: 500 })
console.error("Error in cleanup process:", err);
return new Response(
JSON.stringify({ code: 500, message: err.message, data: null }),
{ headers: { ...corsHeaders, "Content-Type": "application/json" }, status: 500 }
);
}
})
});
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment