mirror of
https://github.com/wekan/wekan.git
synced 2025-12-16 07:20:12 +01:00
Some migrations and mobile fixes.
Some checks failed
Some checks failed
Thanks to xet7 !
This commit is contained in:
parent
bccc22c5fe
commit
30620d0ca4
20 changed files with 2638 additions and 542 deletions
|
|
@ -13,6 +13,7 @@ import FileStoreStrategyFactory, {moveToStorage, rename, STORAGE_NAME_FILESYSTEM
|
|||
// import { STORAGE_NAME_S3 } from '/models/lib/fileStoreStrategy';
|
||||
import { getAttachmentWithBackwardCompatibility, getAttachmentsWithBackwardCompatibility } from './lib/attachmentBackwardCompatibility';
|
||||
import AttachmentStorageSettings from './attachmentStorageSettings';
|
||||
import { generateUniversalAttachmentUrl, cleanFileUrl } from '/models/lib/universalUrlGenerator';
|
||||
|
||||
let attachmentUploadExternalProgram;
|
||||
let attachmentUploadMimeTypes = [];
|
||||
|
|
@ -325,4 +326,15 @@ if (Meteor.isServer) {
|
|||
Attachments.getAttachmentWithBackwardCompatibility = getAttachmentWithBackwardCompatibility;
|
||||
Attachments.getAttachmentsWithBackwardCompatibility = getAttachmentsWithBackwardCompatibility;
|
||||
|
||||
// Override the link method to use universal URLs
|
||||
if (Meteor.isClient) {
|
||||
// Add custom link method to attachment documents
|
||||
Attachments.collection.helpers({
|
||||
link(version = 'original') {
|
||||
// Use universal URL generator for consistent, URL-agnostic URLs
|
||||
return generateUniversalAttachmentUrl(this._id, version);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default Attachments;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { TAPi18n } from '/imports/i18n';
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import FileStoreStrategyFactory, { FileStoreStrategyFilesystem, FileStoreStrategyGridFs, STORAGE_NAME_FILESYSTEM } from '/models/lib/fileStoreStrategy';
|
||||
import { generateUniversalAvatarUrl, cleanFileUrl } from '/models/lib/universalUrlGenerator';
|
||||
|
||||
const filesize = require('filesize');
|
||||
|
||||
|
|
@ -116,7 +117,9 @@ Avatars = new FilesCollection({
|
|||
const isValid = Promise.await(isFileValid(fileObj, avatarsUploadMimeTypes, avatarsUploadSize, avatarsUploadExternalProgram));
|
||||
|
||||
if (isValid) {
|
||||
ReactiveCache.getUser(fileObj.userId).setAvatarUrl(`${formatFleURL(fileObj)}?auth=false&brokenIsFine=true`);
|
||||
// Set avatar URL using universal URL generator (URL-agnostic)
|
||||
const universalUrl = generateUniversalAvatarUrl(fileObj._id);
|
||||
ReactiveCache.getUser(fileObj.userId).setAvatarUrl(universalUrl);
|
||||
} else {
|
||||
Avatars.remove(fileObj._id);
|
||||
}
|
||||
|
|
@ -164,4 +167,15 @@ if (Meteor.isServer) {
|
|||
});
|
||||
}
|
||||
|
||||
// Override the link method to use universal URLs
|
||||
if (Meteor.isClient) {
|
||||
// Add custom link method to avatar documents
|
||||
Avatars.collection.helpers({
|
||||
link(version = 'original') {
|
||||
// Use universal URL generator for consistent, URL-agnostic URLs
|
||||
return generateUniversalAvatarUrl(this._id, version);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default Avatars;
|
||||
|
|
|
|||
194
models/lib/universalUrlGenerator.js
Normal file
194
models/lib/universalUrlGenerator.js
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
/**
|
||||
* Universal URL Generator
|
||||
* Generates file URLs that work regardless of ROOT_URL and PORT settings
|
||||
* Ensures all attachments and avatars are always visible
|
||||
*/
|
||||
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
|
||||
/**
|
||||
* Generate a universal file URL that works regardless of ROOT_URL and PORT
|
||||
* @param {string} fileId - The file ID
|
||||
* @param {string} type - The file type ('attachment' or 'avatar')
|
||||
* @param {string} version - The file version (default: 'original')
|
||||
* @returns {string} - Universal file URL
|
||||
*/
|
||||
export function generateUniversalFileUrl(fileId, type, version = 'original') {
|
||||
if (!fileId) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Always use relative URLs to avoid ROOT_URL and PORT dependencies
|
||||
if (type === 'attachment') {
|
||||
return `/cdn/storage/attachments/${fileId}`;
|
||||
} else if (type === 'avatar') {
|
||||
return `/cdn/storage/avatars/${fileId}`;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a universal attachment URL
|
||||
* @param {string} attachmentId - The attachment ID
|
||||
* @param {string} version - The file version (default: 'original')
|
||||
* @returns {string} - Universal attachment URL
|
||||
*/
|
||||
export function generateUniversalAttachmentUrl(attachmentId, version = 'original') {
|
||||
return generateUniversalFileUrl(attachmentId, 'attachment', version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a universal avatar URL
|
||||
* @param {string} avatarId - The avatar ID
|
||||
* @param {string} version - The file version (default: 'original')
|
||||
* @returns {string} - Universal avatar URL
|
||||
*/
|
||||
export function generateUniversalAvatarUrl(avatarId, version = 'original') {
|
||||
return generateUniversalFileUrl(avatarId, 'avatar', version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean and normalize a file URL to ensure it's universal
|
||||
* @param {string} url - The URL to clean
|
||||
* @param {string} type - The file type ('attachment' or 'avatar')
|
||||
* @returns {string} - Cleaned universal URL
|
||||
*/
|
||||
export function cleanFileUrl(url, type) {
|
||||
if (!url) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Remove any domain, port, or protocol from the URL
|
||||
let cleanUrl = url;
|
||||
|
||||
// Remove protocol and domain
|
||||
cleanUrl = cleanUrl.replace(/^https?:\/\/[^\/]+/, '');
|
||||
|
||||
// Remove ROOT_URL pathname if present
|
||||
if (Meteor.isServer && process.env.ROOT_URL) {
|
||||
try {
|
||||
const rootUrl = new URL(process.env.ROOT_URL);
|
||||
if (rootUrl.pathname && rootUrl.pathname !== '/') {
|
||||
cleanUrl = cleanUrl.replace(rootUrl.pathname, '');
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore URL parsing errors
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize path separators
|
||||
cleanUrl = cleanUrl.replace(/\/+/g, '/');
|
||||
|
||||
// Ensure URL starts with /
|
||||
if (!cleanUrl.startsWith('/')) {
|
||||
cleanUrl = '/' + cleanUrl;
|
||||
}
|
||||
|
||||
// Convert old CollectionFS URLs to new format
|
||||
if (type === 'attachment') {
|
||||
cleanUrl = cleanUrl.replace('/cfs/files/attachments/', '/cdn/storage/attachments/');
|
||||
} else if (type === 'avatar') {
|
||||
cleanUrl = cleanUrl.replace('/cfs/files/avatars/', '/cdn/storage/avatars/');
|
||||
}
|
||||
|
||||
// Remove any query parameters that might cause issues
|
||||
cleanUrl = cleanUrl.split('?')[0];
|
||||
cleanUrl = cleanUrl.split('#')[0];
|
||||
|
||||
return cleanUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a URL is a universal file URL
|
||||
* @param {string} url - The URL to check
|
||||
* @param {string} type - The file type ('attachment' or 'avatar')
|
||||
* @returns {boolean} - True if it's a universal file URL
|
||||
*/
|
||||
export function isUniversalFileUrl(url, type) {
|
||||
if (!url) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type === 'attachment') {
|
||||
return url.includes('/cdn/storage/attachments/') || url.includes('/cfs/files/attachments/');
|
||||
} else if (type === 'avatar') {
|
||||
return url.includes('/cdn/storage/avatars/') || url.includes('/cfs/files/avatars/');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract file ID from a universal file URL
|
||||
* @param {string} url - The URL to extract from
|
||||
* @param {string} type - The file type ('attachment' or 'avatar')
|
||||
* @returns {string|null} - The file ID or null if not found
|
||||
*/
|
||||
export function extractFileIdFromUrl(url, type) {
|
||||
if (!url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let pattern;
|
||||
if (type === 'attachment') {
|
||||
pattern = /\/(?:cdn\/storage\/attachments|cfs\/files\/attachments)\/([^\/\?#]+)/;
|
||||
} else if (type === 'avatar') {
|
||||
pattern = /\/(?:cdn\/storage\/avatars|cfs\/files\/avatars)\/([^\/\?#]+)/;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
const match = url.match(pattern);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a fallback URL for when the primary URL fails
|
||||
* @param {string} fileId - The file ID
|
||||
* @param {string} type - The file type ('attachment' or 'avatar')
|
||||
* @returns {string} - Fallback URL
|
||||
*/
|
||||
export function generateFallbackUrl(fileId, type) {
|
||||
if (!fileId) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Try alternative route patterns
|
||||
if (type === 'attachment') {
|
||||
return `/attachments/${fileId}`;
|
||||
} else if (type === 'avatar') {
|
||||
return `/avatars/${fileId}`;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all possible URLs for a file (for redundancy)
|
||||
* @param {string} fileId - The file ID
|
||||
* @param {string} type - The file type ('attachment' or 'avatar')
|
||||
* @returns {Array<string>} - Array of possible URLs
|
||||
*/
|
||||
export function getAllPossibleUrls(fileId, type) {
|
||||
if (!fileId) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const urls = [];
|
||||
|
||||
// Primary URL
|
||||
urls.push(generateUniversalFileUrl(fileId, type));
|
||||
|
||||
// Fallback URL
|
||||
urls.push(generateFallbackUrl(fileId, type));
|
||||
|
||||
// Legacy URLs for backward compatibility
|
||||
if (type === 'attachment') {
|
||||
urls.push(`/cfs/files/attachments/${fileId}`);
|
||||
} else if (type === 'avatar') {
|
||||
urls.push(`/cfs/files/avatars/${fileId}`);
|
||||
}
|
||||
|
||||
return urls.filter(url => url); // Remove empty URLs
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue