improve editor and add type detection
This commit is contained in:
@@ -1,151 +0,0 @@
|
||||
import {
|
||||
FileMetadata,
|
||||
SearchResponse,
|
||||
UpdateResult,
|
||||
BatchUpdateResponse,
|
||||
} from '../types';
|
||||
|
||||
const API_BASE = '/api/metadata';
|
||||
|
||||
export class MetadataAPI {
|
||||
/**
|
||||
* Search for files by query, hashId, or property
|
||||
*/
|
||||
static async search(params: {
|
||||
query?: string;
|
||||
hashId?: string;
|
||||
property?: string;
|
||||
propertyValue?: string;
|
||||
limit?: number;
|
||||
}): Promise<SearchResponse> {
|
||||
const response = await fetch(`${API_BASE}/search`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(params),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.error || 'Search failed');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get metadata for a specific file by hash ID
|
||||
*/
|
||||
static async getMetadata(hashId: string): Promise<FileMetadata> {
|
||||
const response = await fetch(`${API_BASE}/${hashId}`);
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.error || 'Failed to get metadata');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update complete metadata for a file
|
||||
*/
|
||||
static async updateMetadata(
|
||||
hashId: string,
|
||||
metadata: FileMetadata
|
||||
): Promise<UpdateResult> {
|
||||
const response = await fetch(`${API_BASE}/${hashId}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(metadata),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.error || 'Failed to update metadata');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a specific property for a file
|
||||
*/
|
||||
static async updateProperty(
|
||||
hashId: string,
|
||||
property: string,
|
||||
value: string
|
||||
): Promise<UpdateResult> {
|
||||
const response = await fetch(`${API_BASE}/${hashId}/property`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ property, value }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.error || 'Failed to update property');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch update multiple files
|
||||
*/
|
||||
static async batchUpdate(
|
||||
updates: Array<{
|
||||
hashId: string;
|
||||
metadata?: FileMetadata;
|
||||
properties?: Record<string, string>;
|
||||
}>
|
||||
): Promise<BatchUpdateResponse> {
|
||||
const response = await fetch(`${API_BASE}/batch`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ updates }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.error || 'Batch update failed');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete metadata for a file
|
||||
*/
|
||||
static async deleteMetadata(hashId: string): Promise<void> {
|
||||
const response = await fetch(`${API_BASE}/${hashId}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.error || 'Failed to delete metadata');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all hash IDs
|
||||
*/
|
||||
static async getAllHashIds(): Promise<{ hashIds: string[]; count: number }> {
|
||||
const response = await fetch(`${API_BASE}/hash-ids`);
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.error || 'Failed to get hash IDs');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export type SchemaPrimitive =
|
||||
| 'string'
|
||||
| 'int'
|
||||
| 'float'
|
||||
| 'bool'
|
||||
| 'json-object'
|
||||
| 'json-array'
|
||||
| 'undefined'
|
||||
| 'mixed';
|
||||
|
||||
export type SchemaHint = 'cid' | 'timestamp';
|
||||
export type SchemaKeyHint = 'language-code';
|
||||
|
||||
export interface SchemaFieldInfo {
|
||||
type: SchemaPrimitive;
|
||||
hint?: SchemaHint;
|
||||
key_hint?: SchemaKeyHint;
|
||||
breakdown: Record<string, number>;
|
||||
}
|
||||
|
||||
export interface SchemaResponse {
|
||||
fields: Record<string, SchemaFieldInfo>;
|
||||
generated_at: string;
|
||||
source: 'live' | 'rescan';
|
||||
}
|
||||
|
||||
let cached: Promise<SchemaResponse> | null = null;
|
||||
|
||||
export async function fetchSchema(): Promise<SchemaResponse> {
|
||||
if (!cached) {
|
||||
cached = fetch('/api/schema').then((r) => {
|
||||
if (!r.ok) {
|
||||
cached = null;
|
||||
throw new Error(`schema fetch failed: ${r.status}`);
|
||||
}
|
||||
return r.json();
|
||||
});
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the live schema entry for a field, or undefined if not yet observed.
|
||||
* Loads /api/schema lazily on first mount and caches the result process-wide.
|
||||
*/
|
||||
export function useSchema(): {
|
||||
schema: SchemaResponse | null;
|
||||
getFieldHint: (key: string) => SchemaFieldInfo | undefined;
|
||||
} {
|
||||
const [schema, setSchema] = useState<SchemaResponse | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let alive = true;
|
||||
fetchSchema()
|
||||
.then((s) => {
|
||||
if (alive) setSchema(s);
|
||||
})
|
||||
.catch(() => {
|
||||
// Schema endpoint missing or errored — editor falls back to plugin schemas.
|
||||
});
|
||||
return () => {
|
||||
alive = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const getFieldHint = (key: string): SchemaFieldInfo | undefined => {
|
||||
if (!schema) return undefined;
|
||||
return schema.fields[key];
|
||||
};
|
||||
|
||||
return { schema, getFieldHint };
|
||||
}
|
||||
Reference in New Issue
Block a user