-
Notifications
You must be signed in to change notification settings - Fork 0
Introduce Data Storage API, Pipelines and Drag & Drop #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
74b0911
Implement data storage API for ADAM
HexaField 8d3c741
file sync with adam
HexaField 750ac59
Add file server and update CRUD API for upload management
HexaField 9d1d9a0
Add p2p storage to tool registry
HexaField 475fe4e
Add local storage API
HexaField 490d2b7
Add dropdown for storage mode
HexaField 1e1b46d
bug fixes
HexaField 5fa7a56
make tsc happy
HexaField c100eca
Add pipeline editor and registry
HexaField f9314fe
remove unused files
HexaField 7b8f146
remove global states in favour of fully local state or stateless impl…
HexaField 25cd8d3
type fixes
HexaField f00645d
Restructure files
HexaField b899ccf
Restructure files
HexaField 77621dd
Refactor pipeline handling: migrate to PipelineSpec, add graph conver…
HexaField dec63cd
remove pipeline editor inbuilt output
HexaField 66d6bc4
can edit pipelines with reactflow
HexaField 3e0eb70
add known schemas
HexaField 1648b3f
Add drag and drop with auto transformation to graph page
HexaField efefa7b
Merge branch 'dev' into data-storage-api
HexaField 1e6f7d6
Refactor: LLM created transformer driven multi-source combination
HexaField 9cb63d5
Improve schema inference and matching
HexaField df10e6d
robust canonicalisation
HexaField File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -113,4 +113,6 @@ index.ts.bak | |
|
|
||
| public/projects/* | ||
|
|
||
| public/data/* | ||
| public/data/* | ||
|
|
||
| storage/ | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,184 @@ | ||
| import express, { Request } from 'express' | ||
| import fs from 'fs' | ||
| import https from 'https' | ||
| import path from 'path' | ||
|
|
||
| type QueryParams = { | ||
| source: string | ||
| predicate: string | ||
| } | ||
|
|
||
| type FileParams = QueryParams & { | ||
| target: string | ||
| } | ||
|
|
||
| // Basic config | ||
| const PORT = 8000 | ||
| // Resolve repo-level dev certs; this file lives in packages/projects/projects/hexafield/conjure/ | ||
| const CERT_PATH = path.resolve(__dirname, '../../../../../certs/cert.pem') | ||
| const KEY_PATH = path.resolve(__dirname, '../../../../../certs/key.pem') | ||
|
|
||
| // Local storage directory for uploaded files | ||
| const STORAGE_DIR = path.resolve(__dirname, 'storage') | ||
| fs.mkdirSync(STORAGE_DIR, { recursive: true }) | ||
|
|
||
| // Minimal CORS for local dev | ||
| const corsMiddleware: express.RequestHandler = (req, res, next) => { | ||
| console.log(`[fileserver] ${req.method} ${req.url}`) | ||
| res.header('Access-Control-Allow-Origin', req.headers.origin || '*') | ||
| res.header('Access-Control-Allow-Credentials', 'true') | ||
| res.header('Access-Control-Allow-Methods', 'GET,POST,OPTIONS') | ||
| res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization') | ||
| if (req.method === 'OPTIONS') return res.sendStatus(204) | ||
| next() | ||
| } | ||
|
|
||
| const app = express() | ||
| app.use(corsMiddleware) | ||
| app.use(express.json({ limit: '10mb' })) // JSON only for metadata routes | ||
|
|
||
| const keyFor = ({ source, predicate }: QueryParams) => `${encodeURIComponent(predicate)}__${encodeURIComponent(source)}` | ||
|
|
||
| const pathFor = (q: QueryParams) => path.join(STORAGE_DIR, `${keyFor(q)}.json`) | ||
|
|
||
| const readFile = (q: QueryParams): FileParams | undefined => { | ||
| const path = pathFor(q) | ||
| if (!fs.existsSync(path)) return undefined | ||
| try { | ||
| return JSON.parse(fs.readFileSync(path, 'utf-8')) as FileParams | ||
| } catch { | ||
| return undefined | ||
| } | ||
| } | ||
|
|
||
| const writeFile = (q: QueryParams, data: string) => { | ||
| fs.writeFileSync(pathFor(q), data) | ||
| } | ||
|
|
||
| app.get('/health', (_req, res) => res.status(200).send('ok')) | ||
|
|
||
| app.post('/create', (req: Request, res) => { | ||
| try { | ||
| const { source, predicate, target } = req.body as unknown as FileParams | ||
| if (!source || !predicate || !target) { | ||
| return res.status(400).json({ error: 'Missing required fields' }) | ||
| } | ||
|
|
||
| const q: QueryParams = { source, predicate } | ||
| const finalPath = pathFor(q) | ||
|
|
||
| if (finalPath && fs.existsSync(finalPath)) fs.unlinkSync(finalPath) | ||
|
|
||
| writeFile(q, target) | ||
|
|
||
| return res.status(201).json({ ok: true }) | ||
| } catch (e) { | ||
| console.error('CREATE error', e) | ||
| return res.status(500).json({ error: 'Internal error' }) | ||
| } | ||
| }) | ||
|
|
||
| // Get (download) | ||
| app.post('/get', (req, res) => { | ||
| try { | ||
| const { source, predicate } = req.body as QueryParams | ||
| if (!source || !predicate) return res.status(400).json({ error: 'Missing required fields' }) | ||
|
|
||
| const path = pathFor({ source, predicate }) | ||
| if (!path || !fs.existsSync(path)) return res.status(404).json({ error: 'Not found' }) | ||
|
|
||
| const file = readFile({ source, predicate }) | ||
| if (!file) return res.status(404).json({ error: 'Not found' }) | ||
|
|
||
| return res.status(200).json(file) | ||
| } catch (e) { | ||
| console.error('GET error', e) | ||
| return res.status(500).json({ error: 'Internal error' }) | ||
| } | ||
| }) | ||
|
|
||
| // Find (return results for matching predicate) | ||
| app.post('/find', (req, res) => { | ||
| try { | ||
| const { predicate } = req.body as { predicate?: string } | ||
| if (!predicate) return res.status(400).json({ error: 'Missing required fields' }) | ||
|
|
||
| // Scan storage dir for all .json meta files | ||
| const results: Array<string> = [] | ||
| const files = fs.readdirSync(STORAGE_DIR) | ||
| for (const file of files) { | ||
| if (!file.endsWith('.json')) continue | ||
| const encodedPredicate = encodeURIComponent(predicate) | ||
| if (file.startsWith(encodedPredicate)) { | ||
| results.push(decodeURIComponent(file.slice(encodedPredicate.length + 2, -5))) | ||
| } | ||
| } | ||
| return res.status(200).json({ ok: true, results }) | ||
| } catch (e) { | ||
| console.error('FIND error', e) | ||
| return res.status(500).json({ error: 'Internal error' }) | ||
| } | ||
| }) | ||
|
|
||
| app.post('/has', (req, res) => { | ||
| try { | ||
| const { source, predicate } = req.body as QueryParams | ||
| if (!source || !predicate) return res.status(400).json({ error: 'Missing required fields' }) | ||
|
|
||
| const file = fs.existsSync(pathFor({ source, predicate })) | ||
| if (!file) return res.status(200).json({ ok: false }) | ||
|
|
||
| return res.status(200).json({ ok: true }) | ||
| } catch (e) { | ||
| console.error('HAS error', e) | ||
| return res.status(500).json({ error: 'Internal error' }) | ||
| } | ||
| }) | ||
|
|
||
| // Replace | ||
| app.post('/replace', (req: Request, res) => { | ||
| try { | ||
| const { source, predicate, target } = req.body as unknown as FileParams | ||
| if (!source || !predicate || !target) { | ||
| return res.status(400).json({ error: 'Missing required fields' }) | ||
| } | ||
| const q: QueryParams = { source, predicate } | ||
| const path = pathFor(q) | ||
| if (!path) { | ||
| return res.status(404).json({ error: 'Not found' }) | ||
| } | ||
| // Remove old file | ||
| if (path && fs.existsSync(path)) fs.unlinkSync(path) | ||
|
|
||
| writeFile(q, target) | ||
|
|
||
| return res.status(200).json({ ok: true }) | ||
| } catch (e) { | ||
| console.error('REPLACE error', e) | ||
| return res.status(500).json({ error: 'Internal error' }) | ||
| } | ||
| }) | ||
|
|
||
| // Delete | ||
| app.post('/delete', (req, res) => { | ||
| try { | ||
| const { source, predicate } = req.body as QueryParams | ||
| if (!source || !predicate) return res.status(400).json({ error: 'Missing required fields' }) | ||
| const q: QueryParams = { source, predicate } | ||
|
|
||
| const path = pathFor(q) | ||
| if (fs.existsSync(path)) fs.unlinkSync(path) | ||
|
|
||
| return res.status(200).json({ ok: true }) | ||
| } catch (e) { | ||
| console.error('DELETE error', e) | ||
| return res.status(500).json({ error: 'Internal error' }) | ||
| } | ||
| }) | ||
|
|
||
| // HTTPS server | ||
| const key = fs.readFileSync(KEY_PATH) | ||
| const cert = fs.readFileSync(CERT_PATH) | ||
| https.createServer({ key, cert }, app).listen(PORT, () => { | ||
| console.log(`[fileserver] HTTPS listening on https://localhost:${PORT}`) | ||
| }) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The HTTPS server uses hardcoded certificate paths that may not exist in all environments. Consider adding error handling for missing certificate files or making paths configurable through environment variables.