Supabase storage でクォータ機能を実装したい場合の tips です。
storage.objects テーブルを見ると、metadata に size というメンバーが定義されていますが、取り出しが面倒なので、file_uploads というテーブルを作ります。中身は以下のようになります。
CREATE TABLE file_uploads (
id uuid default getRandomUuid() primary key,
fname text,
uid uuid,
fsize int,
uploaded_at datetime default now()
);
CREATE INDEX file_uploads_qgroup_index file_uploads(uid);
ファイルアップロードプログラムに file_uploads テーブルの更新を追加します。
import { NextRequest, NextResponse } from "next/server"
import { NextApiResponse } from "next"
import { getServerSession } from "next-auth"
import { createClient } from "@supabase/supabase-js"
export async POST(request: NextRequest): Promise<NextApiResponse> {
// JWT を取得
const session = getServerSession(request, response)
if (!session || !session.user) {
return NextResponse.status(401).json({error: {message: "No JWT"}})
}
// ユーザ ID を取得
const uid = session.user.id
// ファイルのアップロード本体
const file = request.body.file
const fsize = file.size
/* (1) */
const supabase = createClient("url", "api-key")
const { sdata: uploadData, serror: uploadError } = supabase.storage.from("bucket").upload(file.name, file)
if (uploadError) {
return NextResponse.status(500).json({error: {message: "Can't upload file"}})
}
// file_uploads テーブルにクォータのための情報を保存
const { error: dbError } = supabase.from("file_uploads").insert([
{fname: file.name, fsize: fsize, uid: uid}
])
if (dbError) {
return NextResponse.status(500).json({error: {message: "DB error"}})
}
return NextResponse.json({data: true})
}
基本的な線はこれでよいのですが、supabase の制限から、view を1つ作成します。
CREATE VIEW file_upload_usage AS
SELECT uid, SUM(fsize) AS total_size FROM file_uploads GROUP BY uid;
ソフトリミット的な使い方をする場合は、バッチで回します。Vercel を使う場合は vercel.cron に起動を仕掛けておくことが出来ます。
import { createClient } from "@supabae/supabase-js"
import { NextRequest, NextResponse } from "next/server"
import { NextApiResponse } from "next"
export default async function GET(req: NextRequst): Promise<NextApiResponse> {
/* 本来は CRON_SECRET を比較するべきだが省略 */
const { data, error } = supabase.from("file_upload_usage").select("*").gte("total_size", TOTAL_SIZE)
if (error) {
return NextResponse.status(500).json({error: {message: "Failed to get upload usage"}})
}
return NextResponse.json({data: data})
}
ハードリミットを設定したい場合は view の total_size と、ファイルの fsize の和をスレッショルドと比較するコードを (1) に挿入することになります。
const { data, error } = supabase.from("file_upload_usage").select("*").eq("uid", uid).single()
if (error) {
return res.status(500).json({error:{message: "failed get file usage"}})
}
if (data.total_size + fsize > THRESHOLD) {
return res.status(400).json({error: {message: "quota exceeded"}})
}
コメントを残す