diff --git a/package.json b/package.json index f72e6cca..265e1bc0 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,8 @@ "ipfs-only-hash": "^4.0.0", "jsonwebtoken": "^8.5.1", "node-fetch": "^3.1.0", - "scyllo": "0.9.8", + "scyllo": "^1.2.0", + "sunflake": "^0.1.0", "typescript": "^4.5.2", "unzipper": "^0.10.11", "use-yup": "^1.1.0", diff --git a/src/Data.ts b/src/Data.ts index 8f3d33bd..66b5d902 100644 --- a/src/Data.ts +++ b/src/Data.ts @@ -15,6 +15,7 @@ export const DB = new ScylloClient<{ client: { contactPoints: [process.env.DB_IP || 'localhost:9042'], localDataCenter: process.env.DB_DATACENTER || 'datacenter1', + keyspace: 'system' }, debug: false, }); @@ -23,8 +24,7 @@ export const initDB = async () => { log.database('Awaiting Connection'); await DB.awaitConnection(); - await DB.createKeyspace('ipfssignal'); - await DB.useKeyspace('ipfssignal'); + await DB.useKeyspace('ipfssignal', true); log.database('Ensuring Tables'); await DB.createTable( @@ -41,7 +41,7 @@ export const initDB = async () => { 'sites', true, { - host: { type: 'text' }, + host: {type: ''}, owner: { type: 'bigint' }, site_id: { type: 'bigint' }, cid: { type: 'text' }, diff --git a/src/lookup/RequestHandler.ts b/src/lookup/RequestHandler.ts index 5f5e2931..69998e01 100644 --- a/src/lookup/RequestHandler.ts +++ b/src/lookup/RequestHandler.ts @@ -1,12 +1,57 @@ import { DB } from '../Data'; -import { - DomainNotFound -} from '../presets/RejectMessages'; +import { DomainNotFound, FileNotFound } from '../presets/RejectMessages'; import { NextHandler } from './NextHandler'; import { request as httpRequest } from 'node:http'; import { join, basename } from 'node:path'; import { log } from '../util/logging'; -import { create } from 'ipfs-http-client'; +import { create, IPFSHTTPClient } from 'ipfs-http-client'; +import { StatResult } from 'ipfs-core-types/src/files'; + +const resolveFile = async ( + ipfs: IPFSHTTPClient, + prefixPath: string, + path: string +): Promise<{ fileType: string; path: string } | undefined> => { + if (path.length === 0) return undefined; + + let fileAtPath = await ipfs.resolve(join(prefixPath, path)); + log.debug('fileAtPath', fileAtPath); + + try { + const ipfsFile = await ipfs.files.stat(join(prefixPath, path), {}); + if (ipfsFile && ipfsFile.type === 'file') { + return { + fileType: basename(path), + path: fileAtPath, + }; + } + + if (ipfsFile.type === 'directory') { + try { + const indexInFolderPath = join(prefixPath, path, 'index.html'); + const indexInFolder = await ipfs.resolve(indexInFolderPath); + log.debug('indexInFolder', indexInFolder); + if (indexInFolder) { + const ipfsIndexInFolder = await ipfs.files.stat(indexInFolderPath, {}); + log.debug('ipfsIndexInFolder', indexInFolder); + if ( + ipfsIndexInFolder && + ipfsIndexInFolder.type === 'file' + ) { + return { + fileType: basename(indexInFolderPath), + path: 'indexInFolder', + }; + } + } + } catch (error) { + log.debug('No index.html found', error); + } + } + } catch {} + + return await resolveFile(ipfs, prefixPath, join(path, '../')); +}; export const handleRequest = NextHandler(async (request, response) => { log.network( @@ -26,42 +71,25 @@ export const handleRequest = NextHandler(async (request, response) => { }); // Verify if file exists on IPFS node - let ogFeds = await ipfs.resolve(join(a.cid, request.path)); - log.debug({ ogFeds }); - - // If directory - let optionalSuffix = ''; - const abc = await ipfs.files.stat(ogFeds, {}); - log.debug(abc); + const nextPath = await resolveFile(ipfs, a.cid, request.path); - let feds = ogFeds; - if (abc.type === 'directory') { - try { - const feds2 = await ipfs.resolve( - join(a.cid, request.path, 'index.html') - ); - optionalSuffix = 'index.html'; - log.debug(feds2); - feds = feds2; - } catch (error) { - log.debug('No index.html found', error); - } - } + if (!nextPath || nextPath.path.length === 0) + return FileNotFound(request.path); - const mimeType = basename(join(request.path, optionalSuffix)); + const mimeType = nextPath.fileType; log.debug({ mimeType }); // Setup headers response.contentType(mimeType); response.setHeader('Cache-Control', 'max-age=60'); if (process.env.ADD_HEADER) { - response.setHeader('x-ipfs-path', '/ipfs/' + abc.cid.toString()); + response.setHeader('x-ipfs-path', nextPath.path); } // Fetch file from IPFS Endpoint await new Promise((accept) => { var contentRequest = httpRequest( - join(process.env.IPFS_IP || 'http://127.0.0.1:8080', feds), + join(process.env.IPFS_IP || 'http://127.0.0.1:8080', nextPath.path), (incomming) => { // for (const a of Object.keys(incomming.headers)) { // if (a.toLowerCase() === 'content-type') continue; @@ -77,6 +105,8 @@ export const handleRequest = NextHandler(async (request, response) => { ); contentRequest.on('error', (error) => { log.error(error); + response.write('oops'); + response.end(); }); contentRequest.end(); }); diff --git a/yarn.lock b/yarn.lock index e9564a13..0067c8e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3317,10 +3317,10 @@ safe-regex@^2.1.1: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -scyllo@0.9.8: - version "0.9.8" - resolved "https://registry.yarnpkg.com/scyllo/-/scyllo-0.9.8.tgz#e35c8045b2bcbbfdbfd6ebef5e1598daf0c3706a" - integrity sha512-NEgP6feeWaUDBnCpXmApNerjUKN8ur84jpYEYCgfI6o8VjGX9FS0utkIYun3yBTkeu4DXs1uYOicDavjhEKsnw== +scyllo@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/scyllo/-/scyllo-1.2.0.tgz#eb71865fa93205a0695908f05c7d27a8499c0106" + integrity sha512-Vp1V/JY9SiDwrfOJUwvZHMERK9wxUg5Wu2QAnZSJhBWAE31AlMD5H6kla3PIJuQjy727bB4yhAURiTeakG+ncg== dependencies: "@lvksh/logger" "^1.4.4" cassandra-driver "^4.6.3" @@ -3560,6 +3560,11 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +sunflake@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/sunflake/-/sunflake-0.1.0.tgz#a92013a9b31d9ff4d327ed759df4566e153ca064" + integrity sha512-uldDTF3h6MDrRXBFe5muMHP4CBSgS0VLrTGoa/RTVELH6eGqlPxWklyctgbdRHQfVhzw5bC5yeWSfam1ZukAEw== + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"