浏览代码

Oasis release 0.3.5

psy 1 天之前
父节点
当前提交
841b57853f
共有 2 个文件被更改,包括 53 次插入65 次删除
  1. 36 58
      src/backend/backend.js
  2. 17 7
      src/views/inhabitants_view.js

+ 36 - 58
src/backend/backend.js

@@ -10,6 +10,7 @@ const os = require('os');
 const promisesFs = require("fs").promises;
 const SSBconfig = require('../server/SSB_server.js');
 const moment = require('../server/node_modules/moment');
+const FileType = require('../server/node_modules/file-type');
 
 const defaultConfig = {};
 const defaultConfigFile = path.join(
@@ -898,70 +899,47 @@ router
   })
   .get("/image/:imageSize/:blobId", async (ctx) => {
     const { blobId, imageSize } = ctx.params;
-    const fakePixel = Buffer.from(
+    const size = Number(imageSize);
+    const fallbackPixel = Buffer.from(
       "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=",
       "base64"
     );
-    const fakeImage = (imageSize) =>
-    sharp
-      ? sharp({
-          create: {
-            width: imageSize,
-            height: imageSize,
-            channels: 4,
-            background: {
-              r: 0,
-              g: 0,
-              b: 0,
-              alpha: 0.5,
-            },
-          },
-        })
-          .png()
-          .toBuffer()
-    : new Promise((resolve) => resolve(fakePixel));
-    const image = async ({ blobId, imageSize }) => {
-    try {
-      if (blobId.startsWith('local://')) {
-        const localImagePath = blobId.slice(7);
-        const resolvedPath = path.resolve(localImagePath);
-        if (fs.existsSync(resolvedPath)) {
-          const buffer = fs.readFileSync(resolvedPath);
-          return buffer; 
-        } else {
-          return fakeImage(imageSize); 
-        }
+    const fakeImage = () => {
+      if (typeof sharp !== "function") {
+        return Promise.resolve(fallbackPixel);
       }
-      const bufferSource = await blob.get({ blobId });
-      if (!bufferSource) {
-        return fakeImage(imageSize);
+      return sharp({
+        create: {
+          width: size,
+          height: size,
+          channels: 4,
+          background: { r: 0, g: 0, b: 0, alpha: 0.5 },
+        },
+      }).png().toBuffer();
+    };
+    try {
+      const buffer = await blob.getResolved({ blobId });
+      if (!buffer) {
+        ctx.set("Content-Type", "image/png");
+        ctx.body = await fakeImage();
+        return;
       }
-      return new Promise((resolve, reject) => {
-        pull(
-          bufferSource,
-          pull.collect((err, bufferArray) => {
-            if (err) {
-              reject(fakeImage(imageSize));
-            } else {
-              const buffer = Buffer.concat(bufferArray);
-              if (sharp) {
-                sharp(buffer)
-                  .resize(imageSize, imageSize)
-                  .png()
-                  .toBuffer()
-                  .then((data) => resolve(data)); 
-                } else {
-                  resolve(buffer);
-                  }
-              }
-            })
-          );
-        });
-      } catch (error) {
-        return fakeImage(imageSize);
+      const fileType = await FileType.fromBuffer(buffer);
+      const mimeType = fileType?.mime || "application/octet-stream";
+      ctx.set("Content-Type", mimeType);
+      if (typeof sharp === "function") {
+        ctx.body = await sharp(buffer)
+          .resize(size, size)
+          .png()
+          .toBuffer();
+      } else {
+        ctx.body = buffer;
       }
-    };
-    ctx.body = await image({ blobId, imageSize: Number(imageSize) });
+    } catch (err) {
+      console.error("Image fetch error:", err);
+      ctx.set("Content-Type", "image/png");
+      ctx.body = await fakeImage();
+    }
   })
   .get("/settings", async (ctx) => {
     const theme = ctx.cookies.get("theme") || "Dark-SNH";

+ 17 - 7
src/views/inhabitants_view.js

@@ -1,6 +1,16 @@
 const { div, h2, p, section, button, form, img, a, textarea, input, br } = require("../server/node_modules/hyperaxe");
 const { template, i18n } = require('./main_views');
 
+function resolvePhoto(photoField, size = 256) {
+  if (typeof photoField === 'string' && photoField.startsWith('/image/')) {
+    return photoField;
+  }
+  if (/^&[A-Za-z0-9+/=]+\.sha256$/.test(photoField)) {
+    return `/image/${size}/${encodeURIComponent(photoField)}`;
+  }
+  return '/assets/images/default-avatar.png';
+}
+
 const generateFilterButtons = (filters, currentFilter) => {
   return filters.map(mode => 
     form({ method: 'GET', action: '/inhabitants' },
@@ -15,10 +25,10 @@ const generateFilterButtons = (filters, currentFilter) => {
 
 const renderInhabitantCard = (user, filter) => {
   return div({ class: 'inhabitant-card' },
-    img({
-      class: 'inhabitant-photo',
-      src: user.photo
-    }),
+  img({
+    class: 'inhabitant-photo',
+    src: resolvePhoto(user.photo),
+  }),
     div({ class: 'inhabitant-details' },
       h2(user.name),
       user.description ? p(user.description) : null,
@@ -40,7 +50,7 @@ const renderGalleryInhabitants = (inhabitants) => {
   return div({ class: "gallery", style: 'display:grid; grid-template-columns: repeat(3, 1fr); gap:16px;' },
     inhabitants.length
       ? inhabitants.map(u => {
-          const photo = u.photo || '/assets/images/default-avatar.png'; 
+          const photo = resolvePhoto(u.photo);
           return a({ href: `#inhabitant-${encodeURIComponent(u.id)}`, class: "gallery-item" },
             img({
               src: photo,
@@ -55,7 +65,7 @@ const renderGalleryInhabitants = (inhabitants) => {
 
 const renderLightbox = (inhabitants) => {
   return inhabitants.map(u => {
-    const photoUrl = u.photo;
+  const photoUrl = resolvePhoto(u.photo);
     return div(
       { id: `inhabitant-${encodeURIComponent(u.id)}`, class: "lightbox" },
       a({ href: "#", class: "lightbox-close" }, "×"),
@@ -143,7 +153,7 @@ exports.inhabitantsProfileView = ({ about = {}, cv = {}, feed = [] }) => {
   const id = cv?.author || about?.about || 'unknown';
   const name = cv?.name || about?.name || 'Unnamed';
   const description = cv?.description || about?.description || '';
-  const image = cv?.photo ? `/image/256/${encodeURIComponent(cv.photo)}` : '/assets/images/snh-oasis.jpg';
+  const image = resolvePhoto(cv?.photo) || '/assets/images/default-oasis.jpg';
   const location = cv?.location || '';
   const languages = typeof cv?.languages === 'string'
     ? cv.languages.split(',').map(x => x.trim()).filter(Boolean)