Fix file sharing load speed and move error; misc updates
- Remove recursive directory size calculations (single Seafile API call per list) - Remove 'Used in this location' usage display - Fix move using v2 per-type endpoints instead of broken batch endpoint - Send entry type from frontend for correct move routing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
import crypto from 'crypto';
|
||||
import { createClient } from '@supabase/supabase-js';
|
||||
|
||||
function json(res, status, body) {
|
||||
res.status(status).setHeader('Content-Type', 'application/json');
|
||||
res.setHeader('Cache-Control', 'no-store');
|
||||
res.send(JSON.stringify(body));
|
||||
}
|
||||
|
||||
async function requireTeam(authHeader) {
|
||||
const supabaseUrl = process.env.VITE_SUPABASE_URL || process.env.SUPABASE_URL;
|
||||
const supabaseAnonKey = process.env.VITE_SUPABASE_ANON_KEY || process.env.SUPABASE_ANON_KEY;
|
||||
|
||||
if (!supabaseUrl || !supabaseAnonKey) {
|
||||
throw new Error('Supabase auth env is not configured on Vercel.');
|
||||
}
|
||||
|
||||
const callerClient = createClient(supabaseUrl, supabaseAnonKey, {
|
||||
auth: { persistSession: false, autoRefreshToken: false },
|
||||
global: { headers: { Authorization: authHeader } },
|
||||
});
|
||||
|
||||
const { data: userData, error: userError } = await callerClient.auth.getUser();
|
||||
if (userError || !userData?.user) {
|
||||
return { ok: false, status: 401, message: 'Unauthorized' };
|
||||
}
|
||||
|
||||
const { data: profile, error: profileError } = await callerClient
|
||||
.from('profiles')
|
||||
.select('role')
|
||||
.eq('id', userData.user.id)
|
||||
.single();
|
||||
|
||||
if (profileError) {
|
||||
return { ok: false, status: 500, message: profileError.message };
|
||||
}
|
||||
|
||||
if (profile?.role !== 'team') {
|
||||
return { ok: false, status: 403, message: 'Forbidden' };
|
||||
}
|
||||
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
function getKey() {
|
||||
const secret = process.env.PASSWORD_VAULT_KEY || '';
|
||||
if (!secret) throw new Error('PASSWORD_VAULT_KEY is not configured on Vercel.');
|
||||
return Buffer.from(secret, 'base64');
|
||||
}
|
||||
|
||||
export default async function handler(req, res) {
|
||||
if (req.method !== 'POST') return json(res, 405, { error: 'Method not allowed' });
|
||||
|
||||
try {
|
||||
const authHeader = req.headers.authorization || '';
|
||||
if (!authHeader) return json(res, 401, { error: 'No authorization header' });
|
||||
|
||||
const auth = await requireTeam(authHeader);
|
||||
if (!auth.ok) return json(res, auth.status, { error: auth.message });
|
||||
|
||||
const { action, plaintext, ciphertext, iv } = req.body || {};
|
||||
const key = getKey();
|
||||
|
||||
if (action === 'encrypt') {
|
||||
if (!plaintext) return json(res, 400, { error: 'plaintext required' });
|
||||
|
||||
const ivBuffer = crypto.randomBytes(12);
|
||||
const cipher = crypto.createCipheriv('aes-256-gcm', key, ivBuffer);
|
||||
const encrypted = Buffer.concat([cipher.update(String(plaintext), 'utf8'), cipher.final()]);
|
||||
const tag = cipher.getAuthTag();
|
||||
const packed = Buffer.concat([encrypted, tag]);
|
||||
|
||||
return json(res, 200, {
|
||||
ciphertext: packed.toString('base64'),
|
||||
iv: ivBuffer.toString('base64'),
|
||||
});
|
||||
}
|
||||
|
||||
if (action === 'decrypt') {
|
||||
if (!ciphertext || !iv) return json(res, 400, { error: 'ciphertext and iv required' });
|
||||
|
||||
const raw = Buffer.from(String(ciphertext), 'base64');
|
||||
const ivBuffer = Buffer.from(String(iv), 'base64');
|
||||
const encrypted = raw.subarray(0, raw.length - 16);
|
||||
const tag = raw.subarray(raw.length - 16);
|
||||
|
||||
const decipher = crypto.createDecipheriv('aes-256-gcm', key, ivBuffer);
|
||||
decipher.setAuthTag(tag);
|
||||
const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
||||
|
||||
return json(res, 200, { plaintext: decrypted.toString('utf8') });
|
||||
}
|
||||
|
||||
return json(res, 400, { error: 'Invalid action' });
|
||||
} catch (error) {
|
||||
return json(res, 500, { error: error.message || 'Unexpected server error' });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user