Spaces:
Running
Running
Upload 13 files
Browse files- index.html +14 -6
- server.js +55 -11
index.html
CHANGED
|
@@ -871,13 +871,15 @@
|
|
| 871 |
getDisplayImage(item) {
|
| 872 |
if (!item) return '';
|
| 873 |
const fullUrl = this.getFullImageUrl(item);
|
|
|
|
|
|
|
| 874 |
if (Device.isMobile) {
|
|
|
|
| 875 |
if (item.displayUrl) return item.displayUrl;
|
| 876 |
-
const source =
|
| 877 |
if (source && source.startsWith('data:image')) {
|
| 878 |
if (source.length > 1200000) {
|
| 879 |
-
|
| 880 |
-
return thumbSrc || fullUrl || PLACEHOLDER_IMAGE;
|
| 881 |
}
|
| 882 |
const objectUrl = this.dataUrlToObjectUrl(source);
|
| 883 |
if (objectUrl) {
|
|
@@ -885,8 +887,14 @@
|
|
| 885 |
return objectUrl;
|
| 886 |
}
|
| 887 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 888 |
}
|
| 889 |
-
return
|
| 890 |
},
|
| 891 |
|
| 892 |
revokeDisplayUrl(item) {
|
|
@@ -1579,7 +1587,7 @@
|
|
| 1579 |
const thumbSrc = resolveAssetUrl(item.thumb || item.thumbUrl);
|
| 1580 |
const fullSrc = ImageHandler.getFullImageUrl(item) || resolveAssetUrl(item.imageUrl);
|
| 1581 |
let displaySrc = thumbSrc || ImageHandler.getDisplayImage(item) || fullSrc;
|
| 1582 |
-
if (Device.isMobile && isPriority && fullSrc) {
|
| 1583 |
displaySrc = fullSrc;
|
| 1584 |
}
|
| 1585 |
if (Device.isMobile && isPriority) {
|
|
@@ -1701,7 +1709,7 @@
|
|
| 1701 |
const thumbSrc = resolveAssetUrl(item.thumb || item.thumbUrl);
|
| 1702 |
const fullSrc = ImageHandler.getFullImageUrl(item) || resolveAssetUrl(item.imageUrl);
|
| 1703 |
let nextSrc = thumbSrc || ImageHandler.getDisplayImage(item) || fullSrc;
|
| 1704 |
-
if (Device.isMobile && item.isNew && fullSrc) {
|
| 1705 |
nextSrc = fullSrc;
|
| 1706 |
}
|
| 1707 |
if (nextSrc) {
|
|
|
|
| 871 |
getDisplayImage(item) {
|
| 872 |
if (!item) return '';
|
| 873 |
const fullUrl = this.getFullImageUrl(item);
|
| 874 |
+
const thumbSrc = resolveAssetUrl(item.thumb || item.thumbUrl);
|
| 875 |
+
const rawSource = resolveAssetUrl(item.image);
|
| 876 |
if (Device.isMobile) {
|
| 877 |
+
if (thumbSrc) return thumbSrc;
|
| 878 |
if (item.displayUrl) return item.displayUrl;
|
| 879 |
+
const source = rawSource || fullUrl;
|
| 880 |
if (source && source.startsWith('data:image')) {
|
| 881 |
if (source.length > 1200000) {
|
| 882 |
+
return fullUrl || source || PLACEHOLDER_IMAGE;
|
|
|
|
| 883 |
}
|
| 884 |
const objectUrl = this.dataUrlToObjectUrl(source);
|
| 885 |
if (objectUrl) {
|
|
|
|
| 887 |
return objectUrl;
|
| 888 |
}
|
| 889 |
}
|
| 890 |
+
return fullUrl || source || '';
|
| 891 |
+
}
|
| 892 |
+
if (rawSource && rawSource.startsWith('data:image') && fullUrl) {
|
| 893 |
+
if (rawSource.length > 400000) {
|
| 894 |
+
return fullUrl;
|
| 895 |
+
}
|
| 896 |
}
|
| 897 |
+
return rawSource || fullUrl || '';
|
| 898 |
},
|
| 899 |
|
| 900 |
revokeDisplayUrl(item) {
|
|
|
|
| 1587 |
const thumbSrc = resolveAssetUrl(item.thumb || item.thumbUrl);
|
| 1588 |
const fullSrc = ImageHandler.getFullImageUrl(item) || resolveAssetUrl(item.imageUrl);
|
| 1589 |
let displaySrc = thumbSrc || ImageHandler.getDisplayImage(item) || fullSrc;
|
| 1590 |
+
if (Device.isMobile && isPriority && fullSrc && !thumbSrc) {
|
| 1591 |
displaySrc = fullSrc;
|
| 1592 |
}
|
| 1593 |
if (Device.isMobile && isPriority) {
|
|
|
|
| 1709 |
const thumbSrc = resolveAssetUrl(item.thumb || item.thumbUrl);
|
| 1710 |
const fullSrc = ImageHandler.getFullImageUrl(item) || resolveAssetUrl(item.imageUrl);
|
| 1711 |
let nextSrc = thumbSrc || ImageHandler.getDisplayImage(item) || fullSrc;
|
| 1712 |
+
if (Device.isMobile && item.isNew && fullSrc && !thumbSrc) {
|
| 1713 |
nextSrc = fullSrc;
|
| 1714 |
}
|
| 1715 |
if (nextSrc) {
|
server.js
CHANGED
|
@@ -403,8 +403,17 @@ const GeneratedImageStore = {
|
|
| 403 |
|
| 404 |
async ensureThumbnailFromUrl(imageUrl) {
|
| 405 |
if (!imageUrl || typeof imageUrl !== 'string') return null;
|
| 406 |
-
|
| 407 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 408 |
const filePath = path.join(GENERATED_DIR, filename);
|
| 409 |
const thumbName = this.buildThumbName(filename);
|
| 410 |
const thumbPath = path.join(THUMB_DIR, thumbName);
|
|
@@ -831,11 +840,19 @@ app.post('/api/generate', authMiddleware, async (req, res) => {
|
|
| 831 |
throw new Error('生成图片保存失败,请稍后重试');
|
| 832 |
}
|
| 833 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 834 |
const responsePayload = {
|
| 835 |
success: true,
|
| 836 |
-
imageUrl,
|
| 837 |
imageId,
|
| 838 |
-
thumbUrl,
|
| 839 |
prompt: trimmedPrompt,
|
| 840 |
inputImages: uploadedImages,
|
| 841 |
timestamp: new Date().toISOString()
|
|
@@ -878,6 +895,13 @@ app.get('/api/public-gallery', async (req, res) => {
|
|
| 878 |
const items = await PublicGalleryStore.getAll();
|
| 879 |
let updated = false;
|
| 880 |
const sanitized = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 881 |
|
| 882 |
for (const item of items) {
|
| 883 |
const { deleteToken, ...rest } = item;
|
|
@@ -921,6 +945,13 @@ app.get('/api/public-gallery', async (req, res) => {
|
|
| 921 |
delete rest.inputImages;
|
| 922 |
}
|
| 923 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 924 |
sanitized.push(rest);
|
| 925 |
}
|
| 926 |
|
|
@@ -1026,13 +1057,26 @@ app.post('/api/public-gallery', authMiddleware, async (req, res) => {
|
|
| 1026 |
deleteToken: generateDeleteToken()
|
| 1027 |
};
|
| 1028 |
|
| 1029 |
-
try {
|
| 1030 |
-
await PublicGalleryStore.add(entry);
|
| 1031 |
-
const { deleteToken, ...publicItem } = entry;
|
| 1032 |
-
|
| 1033 |
-
|
| 1034 |
-
|
| 1035 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1036 |
deleteToken
|
| 1037 |
});
|
| 1038 |
} catch (error) {
|
|
|
|
| 403 |
|
| 404 |
async ensureThumbnailFromUrl(imageUrl) {
|
| 405 |
if (!imageUrl || typeof imageUrl !== 'string') return null;
|
| 406 |
+
let normalizedUrl = imageUrl;
|
| 407 |
+
if (/^https?:\/\//i.test(imageUrl)) {
|
| 408 |
+
try {
|
| 409 |
+
const parsed = new URL(imageUrl);
|
| 410 |
+
normalizedUrl = parsed.pathname;
|
| 411 |
+
} catch {
|
| 412 |
+
normalizedUrl = imageUrl;
|
| 413 |
+
}
|
| 414 |
+
}
|
| 415 |
+
if (!normalizedUrl.startsWith('/generated/')) return null;
|
| 416 |
+
const filename = path.basename(normalizedUrl);
|
| 417 |
const filePath = path.join(GENERATED_DIR, filename);
|
| 418 |
const thumbName = this.buildThumbName(filename);
|
| 419 |
const thumbPath = path.join(THUMB_DIR, thumbName);
|
|
|
|
| 840 |
throw new Error('生成图片保存失败,请稍后重试');
|
| 841 |
}
|
| 842 |
|
| 843 |
+
const hostBase = `${req.protocol}://${req.get('host')}`;
|
| 844 |
+
const toAbsoluteUrl = (value) => {
|
| 845 |
+
if (typeof value === 'string' && value.startsWith('/')) {
|
| 846 |
+
return `${hostBase}${value}`;
|
| 847 |
+
}
|
| 848 |
+
return value;
|
| 849 |
+
};
|
| 850 |
+
|
| 851 |
const responsePayload = {
|
| 852 |
success: true,
|
| 853 |
+
imageUrl: toAbsoluteUrl(imageUrl),
|
| 854 |
imageId,
|
| 855 |
+
thumbUrl: toAbsoluteUrl(thumbUrl),
|
| 856 |
prompt: trimmedPrompt,
|
| 857 |
inputImages: uploadedImages,
|
| 858 |
timestamp: new Date().toISOString()
|
|
|
|
| 895 |
const items = await PublicGalleryStore.getAll();
|
| 896 |
let updated = false;
|
| 897 |
const sanitized = [];
|
| 898 |
+
const hostBase = `${req.protocol}://${req.get('host')}`;
|
| 899 |
+
const toAbsoluteUrl = (value) => {
|
| 900 |
+
if (typeof value === 'string' && value.startsWith('/')) {
|
| 901 |
+
return `${hostBase}${value}`;
|
| 902 |
+
}
|
| 903 |
+
return value;
|
| 904 |
+
};
|
| 905 |
|
| 906 |
for (const item of items) {
|
| 907 |
const { deleteToken, ...rest } = item;
|
|
|
|
| 945 |
delete rest.inputImages;
|
| 946 |
}
|
| 947 |
|
| 948 |
+
if (rest.imageUrl) {
|
| 949 |
+
rest.imageUrl = toAbsoluteUrl(rest.imageUrl);
|
| 950 |
+
}
|
| 951 |
+
if (rest.thumbUrl) {
|
| 952 |
+
rest.thumbUrl = toAbsoluteUrl(rest.thumbUrl);
|
| 953 |
+
}
|
| 954 |
+
|
| 955 |
sanitized.push(rest);
|
| 956 |
}
|
| 957 |
|
|
|
|
| 1057 |
deleteToken: generateDeleteToken()
|
| 1058 |
};
|
| 1059 |
|
| 1060 |
+
try {
|
| 1061 |
+
await PublicGalleryStore.add(entry);
|
| 1062 |
+
const { deleteToken, ...publicItem } = entry;
|
| 1063 |
+
const hostBase = `${req.protocol}://${req.get('host')}`;
|
| 1064 |
+
const toAbsoluteUrl = (value) => {
|
| 1065 |
+
if (typeof value === 'string' && value.startsWith('/')) {
|
| 1066 |
+
return `${hostBase}${value}`;
|
| 1067 |
+
}
|
| 1068 |
+
return value;
|
| 1069 |
+
};
|
| 1070 |
+
if (publicItem.imageUrl) {
|
| 1071 |
+
publicItem.imageUrl = toAbsoluteUrl(publicItem.imageUrl);
|
| 1072 |
+
}
|
| 1073 |
+
if (publicItem.thumbUrl) {
|
| 1074 |
+
publicItem.thumbUrl = toAbsoluteUrl(publicItem.thumbUrl);
|
| 1075 |
+
}
|
| 1076 |
+
recordShareOnce(true);
|
| 1077 |
+
res.json({
|
| 1078 |
+
success: true,
|
| 1079 |
+
item: publicItem,
|
| 1080 |
deleteToken
|
| 1081 |
});
|
| 1082 |
} catch (error) {
|