add supabase file server and downloader

This commit is contained in:
polv committed 2026-03-03 21:52:31 +07:00
1 parent cbef2046fd
commit 811dd73e13
8 files changed
+223 -3

No files matched your search

+1
View File
@@ -13,6 +13,7 @@
"dependencies": { "dependencies": {
"@fastify/mongodb": "^10.0.0", "@fastify/mongodb": "^10.0.0",
"@fastify/static": "^9.0.0", "@fastify/static": "^9.0.0",
"@supabase/supabase-js": "^2.98.0",
"adm-zip": "^0.5.16", "adm-zip": "^0.5.16",
"dotenv": "^17.3.1", "dotenv": "^17.3.1",
"fastify": "^5.7.4", "fastify": "^5.7.4",
+28
View File
@@ -0,0 +1,28 @@
import "dotenv/config";
import { createClient } from "@supabase/supabase-js";
const supabaseUrl = process.env.VITE_SUPABASE_URL!;
const supabasePublishableKey = process.env.VITE_SUPABASE_PUBLISHABLE_KEY!;
const supabase = createClient(supabaseUrl, supabasePublishableKey);
async function main() {
const { data, error } = await supabase.storage.from("yomitan").list("ja");
if (error) throw error;
if (data) {
console.log(data.map((d) => d.id));
const toDownload = data.filter(() => true);
console.log(
toDownload.map((d) =>
supabase.storage.from("yomitan").getPublicUrl(`ja/${d.name}`),
),
);
}
}
if (require.main === module) {
main();
}
+100
View File
@@ -20,6 +20,9 @@ importers:
'@fastify/static': '@fastify/static':
specifier: ^9.0.0 specifier: ^9.0.0
version: 9.0.0 version: 9.0.0
'@supabase/supabase-js':
specifier: ^2.98.0
version: 2.98.0
adm-zip: adm-zip:
specifier: ^0.5.16 specifier: ^0.5.16
version: 0.5.16 version: 0.5.16
@@ -54,6 +57,9 @@ importers:
'@emotion/react': '@emotion/react':
specifier: ^11.14.0 specifier: ^11.14.0
version: 11.14.0(@types/react@19.2.14)(react@19.2.4) version: 11.14.0(@types/react@19.2.14)(react@19.2.4)
'@supabase/supabase-js':
specifier: ^2.98.0
version: 2.98.0
'@tauri-apps/api': '@tauri-apps/api':
specifier: ^2 specifier: ^2
version: 2.10.1 version: 2.10.1
@@ -618,6 +624,30 @@ packages:
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
'@supabase/auth-js@2.98.0':
resolution: {integrity: sha512-GBH361T0peHU91AQNzOlIrjUZw9TZbB9YDRiyFgk/3Kvr3/Z1NWUZ2athWTfHhwNNi8IrW00foyFxQD9IO/Trg==}
engines: {node: '>=20.0.0'}
'@supabase/functions-js@2.98.0':
resolution: {integrity: sha512-N/xEyiNU5Org+d+PNCpv+TWniAXRzxIURxDYsS/m2I/sfAB/HcM9aM2Dmf5edj5oWb9GxID1OBaZ8NMmPXL+Lg==}
engines: {node: '>=20.0.0'}
'@supabase/postgrest-js@2.98.0':
resolution: {integrity: sha512-v6e9WeZuJijzUut8HyXu6gMqWFepIbaeaMIm1uKzei4yLg9bC9OtEW9O14LE/9ezqNbSAnSLO5GtOLFdm7Bpkg==}
engines: {node: '>=20.0.0'}
'@supabase/realtime-js@2.98.0':
resolution: {integrity: sha512-rOWt28uGyFipWOSd+n0WVMr9kUXiWaa7J4hvyLCIHjRFqWm1z9CaaKAoYyfYMC1Exn3WT8WePCgiVhlAtWC2yw==}
engines: {node: '>=20.0.0'}
'@supabase/storage-js@2.98.0':
resolution: {integrity: sha512-tzr2mG+v7ILSAZSfZMSL9OPyIH4z1ikgQ8EcQTKfMRz4EwmlFt3UnJaGzSOxyvF5b+fc9So7qdSUWTqGgeLokQ==}
engines: {node: '>=20.0.0'}
'@supabase/supabase-js@2.98.0':
resolution: {integrity: sha512-Ohc97CtInLwZyiSASz7tT9/Abm/vqnIbO9REp+PivVUII8UZsuI3bngRQnYgJdFoOIwvaEII1fX1qy8x0CyNiw==}
engines: {node: '>=20.0.0'}
'@swc/helpers@0.5.18': '@swc/helpers@0.5.18':
resolution: {integrity: sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==} resolution: {integrity: sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==}
@@ -733,6 +763,9 @@ packages:
'@types/parse-json@4.0.2': '@types/parse-json@4.0.2':
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
'@types/phoenix@1.6.7':
resolution: {integrity: sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==}
'@types/react-dom@19.2.3': '@types/react-dom@19.2.3':
resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==}
peerDependencies: peerDependencies:
@@ -747,6 +780,9 @@ packages:
'@types/whatwg-url@13.0.0': '@types/whatwg-url@13.0.0':
resolution: {integrity: sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==} resolution: {integrity: sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==}
'@types/ws@8.18.1':
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
'@vitejs/plugin-react@4.7.0': '@vitejs/plugin-react@4.7.0':
resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==}
engines: {node: ^14.18.0 || >=16.0.0} engines: {node: ^14.18.0 || >=16.0.0}
@@ -1174,6 +1210,10 @@ packages:
resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
iceberg-js@0.8.1:
resolution: {integrity: sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==}
engines: {node: '>=20.0.0'}
import-fresh@3.3.1: import-fresh@3.3.1:
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
engines: {node: '>=6'} engines: {node: '>=6'}
@@ -1568,6 +1608,18 @@ packages:
resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==}
engines: {node: '>=18'} engines: {node: '>=18'}
ws@8.19.0:
resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
yallist@3.1.1: yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
@@ -2118,6 +2170,44 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.58.0': '@rollup/rollup-win32-x64-msvc@4.58.0':
optional: true optional: true
'@supabase/auth-js@2.98.0':
dependencies:
tslib: 2.8.1
'@supabase/functions-js@2.98.0':
dependencies:
tslib: 2.8.1
'@supabase/postgrest-js@2.98.0':
dependencies:
tslib: 2.8.1
'@supabase/realtime-js@2.98.0':
dependencies:
'@types/phoenix': 1.6.7
'@types/ws': 8.18.1
tslib: 2.8.1
ws: 8.19.0
transitivePeerDependencies:
- bufferutil
- utf-8-validate
'@supabase/storage-js@2.98.0':
dependencies:
iceberg-js: 0.8.1
tslib: 2.8.1
'@supabase/supabase-js@2.98.0':
dependencies:
'@supabase/auth-js': 2.98.0
'@supabase/functions-js': 2.98.0
'@supabase/postgrest-js': 2.98.0
'@supabase/realtime-js': 2.98.0
'@supabase/storage-js': 2.98.0
transitivePeerDependencies:
- bufferutil
- utf-8-validate
'@swc/helpers@0.5.18': '@swc/helpers@0.5.18':
dependencies: dependencies:
tslib: 2.8.1 tslib: 2.8.1
@@ -2216,6 +2306,8 @@ snapshots:
'@types/parse-json@4.0.2': {} '@types/parse-json@4.0.2': {}
'@types/phoenix@1.6.7': {}
'@types/react-dom@19.2.3(@types/react@19.2.14)': '@types/react-dom@19.2.3(@types/react@19.2.14)':
dependencies: dependencies:
'@types/react': 19.2.14 '@types/react': 19.2.14
@@ -2230,6 +2322,10 @@ snapshots:
dependencies: dependencies:
'@types/webidl-conversions': 7.0.3 '@types/webidl-conversions': 7.0.3
'@types/ws@8.18.1':
dependencies:
'@types/node': 24.10.13
'@vitejs/plugin-react@4.7.0(vite@7.3.1(@types/node@24.10.13)(tsx@4.21.0))': '@vitejs/plugin-react@4.7.0(vite@7.3.1(@types/node@24.10.13)(tsx@4.21.0))':
dependencies: dependencies:
'@babel/core': 7.29.0 '@babel/core': 7.29.0
@@ -3005,6 +3101,8 @@ snapshots:
statuses: 2.0.2 statuses: 2.0.2
toidentifier: 1.0.1 toidentifier: 1.0.1
iceberg-js@0.8.1: {}
import-fresh@3.3.1: import-fresh@3.3.1:
dependencies: dependencies:
parent-module: 1.0.1 parent-module: 1.0.1
@@ -3312,6 +3410,8 @@ snapshots:
tr46: 5.1.1 tr46: 5.1.1
webidl-conversions: 7.0.0 webidl-conversions: 7.0.0
ws@8.19.0: {}
yallist@3.1.1: {} yallist@3.1.1: {}
yaml@1.10.2: {} yaml@1.10.2: {}
+1 -1
View File
@@ -82,7 +82,7 @@ impl AppService {
rusqlite::types::ValueRef::Text(t) => { rusqlite::types::ValueRef::Text(t) => {
serde_json::Value::String(String::from_utf8_lossy(t).into()) serde_json::Value::String(String::from_utf8_lossy(t).into())
} }
_ => serde_json::Value::Null, rusqlite::types::ValueRef::Blob(_) => serde_json::Value::Null,
}; };
obj.insert(name.clone(), value); obj.insert(name.clone(), value);
} }
+1
View File
@@ -11,6 +11,7 @@
"dependencies": { "dependencies": {
"@chakra-ui/react": "^3.33.0", "@chakra-ui/react": "^3.33.0",
"@emotion/react": "^11.14.0", "@emotion/react": "^11.14.0",
"@supabase/supabase-js": "^2.98.0",
"@tauri-apps/api": "^2", "@tauri-apps/api": "^2",
"@tauri-apps/plugin-fs": "~2.4.5", "@tauri-apps/plugin-fs": "~2.4.5",
"@tauri-apps/plugin-http": "~2.5.7", "@tauri-apps/plugin-http": "~2.5.7",
+74 -1
View File
@@ -3,6 +3,14 @@ import { useEffect, useRef, useState } from "react";
import { Dialog, Portal } from "@chakra-ui/react"; import { Dialog, Portal } from "@chakra-ui/react";
import { invoke } from "@tauri-apps/api/core"; import { invoke } from "@tauri-apps/api/core";
import { listen } from "@tauri-apps/api/event"; import { listen } from "@tauri-apps/api/event";
import {
BaseDirectory,
exists,
readDir,
writeFile,
} from "@tauri-apps/plugin-fs";
import { supabase } from "../lib/supabaseClient";
interface LoadYomitanZipDirResult { interface LoadYomitanZipDirResult {
new_dicts: string[]; new_dicts: string[];
@@ -31,7 +39,26 @@ function LoadingDialog() {
if (isInit.current) return; if (isInit.current) return;
isInit.current = true; isInit.current = true;
invoke("init_yomitan").then(() => { invoke("init_yomitan").then(async () => {
const { data, error } = await supabase.storage.from("yomitan").list("ja");
if (error) throw error;
for (const d of data || []) {
const filepath = `yomitan/ja/${d.name}`;
if (
await exists(filepath, {
baseDir: BaseDirectory.AppData,
})
)
continue;
const {
data: { publicUrl },
} = supabase.storage.from("yomitan").getPublicUrl(`ja/${d.name}`);
// await downloadURL(publicUrl, filepath, (received, total) => {});
}
setMessages([]); setMessages([]);
}); });
@@ -105,3 +132,49 @@ function LoadingDialog() {
} }
export default LoadingDialog; export default LoadingDialog;
async function downloadURL(
url: string,
filepath: string,
progressCallback: (received: number, total: number) => void,
) {
const r = await fetch(url);
if (!r.ok) throw r;
if (!r.body) throw r;
const contentLength = r.headers.get("Content-Length");
const total = contentLength ? parseInt(contentLength) : 0;
let receivedBytes = 0;
const reader = r.body.getReader();
const stream = new ReadableStream({
start(controller) {
function push() {
reader
.read()
.then(({ done, value }) => {
if (done) {
controller.close();
return;
}
receivedBytes += value.length;
// Calculate and log progress
if (total) progressCallback(receivedBytes, total);
controller.enqueue(value);
push(); // Read the next chunk
})
.catch((error) => {
console.error("Stream reading error:", error);
controller.error(error);
});
}
push(); // Start reading the data
},
});
await writeFile(filepath, stream, { baseDir: BaseDirectory.AppData });
}
+6
View File
@@ -0,0 +1,6 @@
import { createClient } from "@supabase/supabase-js";
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabasePublishableKey = import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY;
export const supabase = createClient(supabaseUrl, supabasePublishableKey);
+12 -1
View File
@@ -16,6 +16,17 @@
} }
] ]
}, },
"fs:default" "fs:allow-exists",
"fs:allow-appdata-write",
"fs:allow-appdata-meta",
{
"identifier": "fs:scope",
"allow": [
"$APPDATA/**/*.zip"
],
"deny": [
"$APPDATA/*.db"
]
}
] ]
} }