feat(mindspace): 公开页分享组件、workspace 路径与 Plaza 发布增强
- 新增 public share widget 与 workspace relative path 解析 - 增强 publications/chat-plaza 发布链路与缩略图 demo - 补充 schema、cover 检查脚本与回归测试 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+65
-7
@@ -25,6 +25,10 @@ import {
|
||||
DEFAULT_IMAGE_UPLOAD_MAX_BYTES,
|
||||
normalizeImageForStorage,
|
||||
} from './user-image-normalize.mjs';
|
||||
import {
|
||||
normalizeWorkspaceRelativePath,
|
||||
resolveAssetWorkspaceRelativePath,
|
||||
} from './mindspace-workspace-path.mjs';
|
||||
|
||||
const ALLOWED_EXTENSIONS = new Map([
|
||||
['.txt', 'text/plain'],
|
||||
@@ -184,9 +188,15 @@ function isPublicImageAsset(row) {
|
||||
return row?.category_code === 'public';
|
||||
}
|
||||
|
||||
function isCanonicalPublicImageStorageKey(storageKey) {
|
||||
const normalized = String(storageKey ?? '').replace(/\\/g, '/');
|
||||
if (isWorkspaceStorageKey(normalized)) return false;
|
||||
return isPublicImageStorageKey(normalized);
|
||||
}
|
||||
|
||||
function publicUrlForAsset(row) {
|
||||
if (!isPublicImageAsset(row)) return null;
|
||||
if (row?.storage_key && isPublicImageStorageKey(row.storage_key)) {
|
||||
if (row?.storage_key && isCanonicalPublicImageStorageKey(row.storage_key)) {
|
||||
return buildUserImagePublicUrl({
|
||||
userId: row.user_id,
|
||||
storageKey: row.storage_key,
|
||||
@@ -194,9 +204,16 @@ function publicUrlForAsset(row) {
|
||||
originalFilename: row.original_filename,
|
||||
});
|
||||
}
|
||||
const workspacePath = normalizeWorkspaceRelativePath(row?.workspace_relative_path);
|
||||
if (workspacePath && isWorkspaceStorageKey(row?.storage_key)) {
|
||||
return createMindSpacePublicUrl({
|
||||
publicBaseUrl: resolvePublicBaseUrl(),
|
||||
ownerKey: row.user_id,
|
||||
relativePath: workspacePath,
|
||||
});
|
||||
}
|
||||
if (row?.category_code === 'public' && row?.id) {
|
||||
const legacy = publicTempImageUrl(row.user_id, row.id, row.mime_type, row.original_filename);
|
||||
return legacy;
|
||||
return publicTempImageUrl(row.user_id, row.id, row.mime_type, row.original_filename);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -651,12 +668,17 @@ export function createAssetService(pool, options = {}) {
|
||||
|
||||
const now = Date.now();
|
||||
const visibility = shouldPublishImage ? 'public_candidate' : visibilityForCategory(upload.category_code);
|
||||
const indexedWorkspacePath = resolveAssetWorkspaceRelativePath({
|
||||
categoryCode: upload.category_code,
|
||||
originalFilename: storedFilename,
|
||||
explicitPath: workspaceRelativePath,
|
||||
});
|
||||
await conn.query(
|
||||
`INSERT INTO h5_assets
|
||||
(id, user_id, space_id, category_id, asset_type, mime_type, original_filename,
|
||||
display_name, current_version_id, size_bytes, checksum, risk_level, visibility,
|
||||
display_name, workspace_relative_path, current_version_id, size_bytes, checksum, risk_level, visibility,
|
||||
status, source_type, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'upload', ?, ?)`,
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'upload', ?, ?)`,
|
||||
[
|
||||
assetId,
|
||||
userId,
|
||||
@@ -666,6 +688,7 @@ export function createAssetService(pool, options = {}) {
|
||||
storedMimeType,
|
||||
storedFilename,
|
||||
upload.filename,
|
||||
indexedWorkspacePath,
|
||||
versionId,
|
||||
storedSizeBytes,
|
||||
storedChecksum,
|
||||
@@ -1191,12 +1214,17 @@ export function createAssetService(pool, options = {}) {
|
||||
}
|
||||
const now = Date.now();
|
||||
const visibility = shouldPublishImage ? 'public_candidate' : visibilityForCategory(category.category_code);
|
||||
const indexedWorkspacePath = resolveAssetWorkspaceRelativePath({
|
||||
categoryCode: category.category_code,
|
||||
originalFilename: storedFilename,
|
||||
explicitPath: workspaceRelativePath,
|
||||
});
|
||||
await conn.query(
|
||||
`INSERT INTO h5_assets
|
||||
(id, user_id, space_id, category_id, asset_type, mime_type, original_filename,
|
||||
display_name, current_version_id, size_bytes, checksum, risk_level, visibility,
|
||||
display_name, workspace_relative_path, current_version_id, size_bytes, checksum, risk_level, visibility,
|
||||
status, source_type, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[
|
||||
assetId,
|
||||
userId,
|
||||
@@ -1206,6 +1234,7 @@ export function createAssetService(pool, options = {}) {
|
||||
storedMimeType,
|
||||
storedFilename,
|
||||
displayName || path.basename(storedFilename),
|
||||
indexedWorkspacePath,
|
||||
versionId,
|
||||
storedSizeBytes,
|
||||
storedChecksum,
|
||||
@@ -1370,6 +1399,33 @@ export function createAssetService(pool, options = {}) {
|
||||
return expired;
|
||||
};
|
||||
|
||||
const findAssetByWorkspaceRelativePath = async (userId, relativePath) => {
|
||||
const normalized = normalizeWorkspaceRelativePath(relativePath);
|
||||
if (!normalized) return null;
|
||||
const [rows] = await pool.query(
|
||||
`SELECT a.*, c.category_code, av.storage_key
|
||||
FROM h5_assets a
|
||||
JOIN h5_space_categories c ON c.id = a.category_id AND c.user_id = a.user_id
|
||||
JOIN h5_asset_versions av ON av.id = a.current_version_id
|
||||
WHERE a.user_id = ?
|
||||
AND a.status <> 'deleted'
|
||||
AND (
|
||||
a.workspace_relative_path = ?
|
||||
OR (
|
||||
(a.workspace_relative_path IS NULL OR a.workspace_relative_path = '')
|
||||
AND (
|
||||
a.original_filename = ?
|
||||
OR CONCAT(c.category_code, '/', a.original_filename) = ?
|
||||
)
|
||||
)
|
||||
)
|
||||
ORDER BY a.updated_at DESC, a.id DESC
|
||||
LIMIT 1`,
|
||||
[userId, normalized, normalized, normalized],
|
||||
);
|
||||
return rows[0] ? assetResponse(rows[0]) : null;
|
||||
};
|
||||
|
||||
return {
|
||||
storageRoot,
|
||||
createUpload,
|
||||
@@ -1379,6 +1435,7 @@ export function createAssetService(pool, options = {}) {
|
||||
cancelUpload,
|
||||
createChatAsset,
|
||||
listAssets,
|
||||
findAssetByWorkspaceRelativePath,
|
||||
deleteAsset,
|
||||
readAsset,
|
||||
readPublicAsset,
|
||||
@@ -1440,5 +1497,6 @@ export const assetInternals = {
|
||||
normalizeFilename,
|
||||
expectedMimeType,
|
||||
assetTypeForMime,
|
||||
publicUrlForAsset,
|
||||
registerUploadArtifactForConversation,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user