zrcadlo https://github.com/atscan/atscan
104 řádky
3.3 KiB
JavaScript
104 řádky
3.3 KiB
JavaScript
import { ATScan } from "./lib/atscan.js";
|
|
import { Application, Router } from "https://deno.land/x/oak/mod.ts";
|
|
import { oakCors } from "https://deno.land/x/cors/mod.ts";
|
|
import { join } from "https://deno.land/std@0.192.0/path/posix.ts";
|
|
import { exists } from "https://deno.land/std@0.193.0/fs/exists.ts";
|
|
import { ensureDir } from "https://deno.land/std@0.192.0/fs/ensure_dir.ts";
|
|
import { Sha256 } from "https://deno.land/std@0.119.0/hash/sha256.ts";
|
|
//import imageDecode from "https://deno.land/x/wasm_image_decoder@v0.0.7/mod.js";
|
|
//import wasm_image_loader from 'npm:@saschazar/wasm-image-loader';
|
|
//import wasm_webp from 'npm:@saschazar/wasm-webp';
|
|
//import wasm_webp_options from 'npm:@saschazar/wasm-webp/options';
|
|
|
|
//const imageLoader = await wasm_image_loader();
|
|
//const webp = await wasm_webp();
|
|
|
|
//import * as zstd from "https://deno.land/x/zstd_wasm/deno/zstd.ts";
|
|
//await zstd.init();
|
|
|
|
const ats = new ATScan();
|
|
await ats.init();
|
|
ats.startDaemon();
|
|
|
|
const HTTP_PORT = ats.env.PORT || 6680;
|
|
const app = new Application();
|
|
const router = new Router();
|
|
|
|
function perf(ctx) {
|
|
if (ctx.request.url.toString().startsWith("http://localhost:")) {
|
|
return null;
|
|
}
|
|
console.log(
|
|
`[${HTTP_PORT}] GET ${ctx.request.url} [${
|
|
performance.now() - ctx.perf
|
|
}ms] ${ctx.request.headers.get("user-agent")}`,
|
|
);
|
|
}
|
|
|
|
router
|
|
.get("/:did/:cid", async (ctx) => {
|
|
const { did, cid } = ctx.params;
|
|
if (!cid || !cid.startsWith("baf")) {
|
|
return ctx.status = 404;
|
|
}
|
|
const item = await ats.db.did.findOne({ did });
|
|
if (!item || !item.pds) {
|
|
return ctx.status = 404;
|
|
}
|
|
|
|
let index = null;
|
|
let body = null;
|
|
const blobDir = join(ats.env.ATSCAN_DB_PATH, "blob", did);
|
|
await ensureDir(blobDir);
|
|
const cacheIndexFn = join(blobDir, `${cid}.json`);
|
|
const cacheFn = join(blobDir, `${cid}.blob`);
|
|
|
|
if (!await exists(cacheIndexFn)) {
|
|
const src = `${
|
|
item.pds[0]
|
|
}/xrpc/com.atproto.sync.getBlob?did=${did}&cid=${cid}`;
|
|
const blobRes = await fetch(src);
|
|
if (!blobRes.ok) {
|
|
return ctx.status = 404;
|
|
}
|
|
body = new Uint8Array(await blobRes.arrayBuffer());
|
|
//const compressed = zstd.compress(body);
|
|
index = {
|
|
src,
|
|
did,
|
|
cid,
|
|
hash: new Sha256().update(body).hex(),
|
|
time: new Date(),
|
|
size: body.length,
|
|
//sizeCompressed: compressed.length,
|
|
headers: {
|
|
"content-length": blobRes.headers.get("content-length"),
|
|
"content-type": blobRes.headers.get("content-type"),
|
|
},
|
|
};
|
|
await Deno.writeTextFile(cacheIndexFn, JSON.stringify(index, null, 2));
|
|
await Deno.writeFile(cacheFn, body);
|
|
} else {
|
|
index = JSON.parse(await Deno.readTextFile(cacheIndexFn));
|
|
//body = zstd.decompress(await Deno.readFile(cacheFn));
|
|
body = await Deno.readFile(cacheFn);
|
|
}
|
|
|
|
for (const headerKey of Object.keys(index.headers)) {
|
|
ctx.response.headers.set(headerKey, index.headers[headerKey]);
|
|
}
|
|
ctx.response.body = body;
|
|
//perf(ctx);
|
|
});
|
|
|
|
app.use(oakCors()); // Enable CORS for All Routes
|
|
app.use(async (ctx, next) => {
|
|
ctx.perf = performance.now();
|
|
await next();
|
|
});
|
|
app.use(router.routes());
|
|
|
|
app.listen({ port: HTTP_PORT });
|
|
|
|
console.log(`ATScan API started at port ${HTTP_PORT}`);
|