ihub-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,31 @@
1
+ import { MongoClient, ServerApiVersion } from 'mongodb';
2
+ const uri = "mongodb+srv://junaid:junaid@cluster0.0ec6y.mongodb.net/?appName=Cluster0";
3
+
4
+
5
+ const client = new MongoClient(uri, {
6
+ serverApi: {
7
+ version: ServerApiVersion.v1,
8
+ strict: true,
9
+ deprecationErrors: true,
10
+ }
11
+ });
12
+
13
+ async function runConnectionTest() {
14
+ try {
15
+
16
+ await client.connect();
17
+ await client.db("admin").command({ ping: 1 });
18
+ console.log("Pinged your deployment. You successfully connected to MongoDB!");
19
+ } catch (error) {
20
+ console.error("Connection error:", error);
21
+ }
22
+ }
23
+
24
+ runConnectionTest();
25
+
26
+
27
+ function provideClient() {
28
+ return client;
29
+ }
30
+
31
+ export {provideClient}
package/index.js ADDED
@@ -0,0 +1,462 @@
1
+ #!/usr/bin/env node
2
+ import path from "path";
3
+ import os from "os";
4
+ import * as crypto from "crypto"
5
+ import yargs from 'yargs';
6
+ import { hideBin } from 'yargs/helpers';
7
+ import { execSync } from "child_process";
8
+ import fs from "fs"
9
+ import dirtoFileArray from "./recursive_file_extractor.js"
10
+ import { PinataSDK } from "pinata";
11
+ import { provideClient } from "./dbconnection.js"
12
+
13
+
14
+
15
+
16
+ const IHUB_DIR = path.join(os.homedir(), ".ihub");
17
+ const FILE_TO_STORE_LOGIN = path.join(IHUB_DIR, "login.txt");
18
+
19
+
20
+
21
+ async function getcreds() {
22
+
23
+ let request=await fetch("https://immutablehub-creds.hf.space/creds",{
24
+ mode:"cors",
25
+ method:"get",
26
+ headers:{
27
+ "content-type":"application/json"
28
+ }
29
+ })
30
+ let response=await request.json()
31
+ return {"jwt":response.jwt,"gateway":response.gateway}
32
+
33
+ }
34
+
35
+ async function getRuntime() {
36
+ const { jwt, gateway } = await getcreds();
37
+ const pinata = new PinataSDK({ pinataJwt: jwt, pinataGateway: gateway });
38
+ const client = provideClient();
39
+ return { pinata, client };
40
+ }
41
+
42
+
43
+
44
+
45
+
46
+ function setUp(wallet) {
47
+
48
+
49
+
50
+ if (!fs.existsSync(IHUB_DIR)) {
51
+ fs.mkdirSync(IHUB_DIR, { recursive: true });
52
+
53
+ }
54
+
55
+ if(fs.existsSync(FILE_TO_STORE_LOGIN)){
56
+ console.log("already loggedin")
57
+ }
58
+ else {
59
+
60
+ fs.writeFileSync(FILE_TO_STORE_LOGIN, wallet)
61
+ console.log(`Successfully wrote wallet data to ${FILE_TO_STORE_LOGIN}`);
62
+
63
+
64
+ }
65
+ }
66
+
67
+
68
+
69
+ function fileWithExtensionExists(extension,folder) {
70
+ try {
71
+ const files = fs.readdirSync(folder);
72
+ const exists = files.some(fileName =>
73
+ fileName.endsWith(extension)
74
+ );
75
+
76
+ return exists;
77
+
78
+ } catch (error) {
79
+ console.error(`Error checking directory: ${error.message}`);
80
+ return false;
81
+ }
82
+ }
83
+
84
+
85
+
86
+
87
+
88
+
89
+
90
+ async function deleteManifest(targetManifestId, foldername,client) {
91
+
92
+
93
+ const db = client.db("ihub_db");
94
+ const coll = db.collection("ihub_col");
95
+ await coll.updateOne(
96
+ { owner: "system" },
97
+ {
98
+ $pull: {
99
+ manifests: {
100
+ id: targetManifestId,
101
+ folder: foldername
102
+ }
103
+ }
104
+ }
105
+ );
106
+
107
+ }
108
+
109
+
110
+
111
+ async function manifestExists(targetManifestId, foldername,client) {
112
+
113
+ const db = client.db("ihub_db");
114
+ const coll = db.collection("ihub_col");
115
+ const doc = await coll.findOne(
116
+ {
117
+ owner: "system",
118
+ manifests: {
119
+ $elemMatch: {
120
+ id: targetManifestId,
121
+ folder: foldername
122
+ }
123
+ }
124
+ },
125
+ { projection: { _id: 1 }}
126
+ );
127
+ return !!doc;
128
+ }
129
+
130
+
131
+
132
+
133
+
134
+
135
+
136
+ async function getFile(folder,cid,pinata) {
137
+
138
+ const result = await pinata.gateways.public.get(cid)
139
+ const Data = result.data;
140
+ console.log(Data)
141
+ const arrayBuffer = await Data.arrayBuffer()
142
+ const buffer = Buffer.from(arrayBuffer);
143
+ console.log(buffer)
144
+ let dynamicstring=crypto.randomUUID()
145
+ let shortID = dynamicstring.substring(0, 5)
146
+ let bpath=`${shortID}.history.bundle`
147
+
148
+ fs.writeFileSync(`${bpath}`,buffer);
149
+
150
+
151
+ if (fs.existsSync(folder)){
152
+ fs.rmSync(folder, { recursive: true, force: true });
153
+ }
154
+
155
+
156
+ fs.mkdirSync(folder, { recursive: true });
157
+
158
+ execSync(`git clone ${bpath} ${folder}`, {
159
+ cwd: ".",
160
+ stdio: 'inherit'
161
+ });
162
+ console.log("cloned successfully")
163
+
164
+ }
165
+
166
+
167
+
168
+ async function getFileNew(folder,obj,pinata) {
169
+
170
+ const result = await pinata.gateways.public.get(obj.cid)
171
+ const Data = result.data;
172
+
173
+ const filePath = path.join(folder, obj.name);
174
+ fs.writeFileSync(`${filePath}`,Data,"utf-8");
175
+ console.log("successfully")
176
+
177
+
178
+ }
179
+
180
+
181
+ function deleteFilesWithExtension(extension, folder) {
182
+ try {
183
+ const files = fs.readdirSync(folder);
184
+
185
+ for (const file of files) {
186
+ if (file.endsWith(extension)) {
187
+ const fullPath = path.join(folder, file);
188
+ fs.unlinkSync(fullPath);
189
+ console.log(`🗑️ Deleted old bundle: ${file}`);
190
+ }
191
+ }
192
+ } catch (err) {
193
+ console.error(`Error deleting bundle files: ${err.message}`);
194
+ }
195
+ }
196
+
197
+
198
+ function gitBundler(FOLDER_TO_UPLOAD){
199
+
200
+
201
+ let dynamicstring=crypto.randomUUID()
202
+ let shortID = dynamicstring.substring(0, 5)
203
+ let bpath=`${shortID}.history.bundle`
204
+ let patternexists=fileWithExtensionExists(".history.bundle",FOLDER_TO_UPLOAD);
205
+ if(patternexists){
206
+ deleteFilesWithExtension(".history.bundle", FOLDER_TO_UPLOAD);
207
+ }
208
+
209
+ const bundlePath = path.join(FOLDER_TO_UPLOAD,bpath);
210
+ execSync(`git bundle create ${path.basename(bundlePath)} --all`, {
211
+ cwd: FOLDER_TO_UPLOAD,
212
+ stdio: 'inherit'
213
+ });
214
+ const bundleSize = fs.statSync(bundlePath).size;
215
+ console.log(`✓ Bundle created: ${(bundleSize / 1024 / 1024).toFixed(2)} MB`);
216
+ console.log(` Location: ${bundlePath}\n`);
217
+
218
+
219
+ }
220
+
221
+
222
+ async function Clone(folder,pinata,client){
223
+
224
+
225
+ const db = client.db("ihub_db");
226
+ const coll = db.collection("ihub_col");
227
+ const targetManifestId= fs.readFileSync(FILE_TO_STORE_LOGIN,'utf8');
228
+ const doc = await coll.findOne({
229
+ "owner": "system",
230
+ "manifests.id": targetManifestId
231
+ });
232
+ if(doc) {
233
+
234
+ let uploads=null
235
+ let bundle=null
236
+ let manifests=doc.manifests
237
+
238
+
239
+ for(let obj of manifests){
240
+
241
+ let dbfolder=String(obj.folder)
242
+
243
+ if(obj.id==targetManifestId && dbfolder==folder){
244
+ console.log(obj.folder)
245
+ uploads=obj.uploads
246
+ }
247
+
248
+
249
+
250
+ }
251
+ console.log(uploads)
252
+
253
+ for(let obj of uploads){
254
+
255
+ let name=obj.name
256
+ const isIncluded = name.includes(".history.bundle");
257
+ if(isIncluded) {
258
+
259
+ bundle=obj.cid
260
+
261
+ }}
262
+
263
+ await getFile(folder,bundle,pinata)
264
+
265
+ }}
266
+
267
+
268
+
269
+
270
+
271
+ async function CloneNew(folder,pinata,client){
272
+
273
+
274
+ const db = client.db("ihub_db");
275
+ const coll = db.collection("ihub_col");
276
+ const targetManifestId= fs.readFileSync(FILE_TO_STORE_LOGIN,'utf8');
277
+ const doc = await coll.findOne({
278
+ "owner": "system",
279
+ "manifests.id": targetManifestId
280
+ });
281
+ if(doc) {
282
+
283
+ let uploads=null
284
+ let manifests=doc.manifests
285
+
286
+
287
+ for(let obj of manifests){
288
+
289
+ let dbfolder=String(obj.folder)
290
+
291
+ if(obj.id==targetManifestId && dbfolder==folder){
292
+ console.log(obj.folder)
293
+ uploads=obj.uploads
294
+ fs.mkdirSync(folder)
295
+ }
296
+
297
+
298
+
299
+ }
300
+ console.log(uploads)
301
+
302
+ for(let obj of uploads){
303
+
304
+ await getFileNew(folder,obj,pinata)
305
+
306
+ }
307
+
308
+ }}
309
+
310
+
311
+
312
+
313
+
314
+
315
+ async function Push(FOLDER_TO_UPLOAD,pinata,client) {
316
+
317
+ try {
318
+
319
+
320
+ const db = client.db("ihub_db");
321
+ const coll = db.collection("ihub_col");
322
+
323
+ let uploads=[]
324
+ let files=dirtoFileArray(FOLDER_TO_UPLOAD)
325
+
326
+ for (let file of files) {
327
+
328
+ const upload=await pinata.upload.public.file(file)
329
+ uploads.push(upload)
330
+ }
331
+
332
+
333
+ console.log(uploads)
334
+ const data = fs.readFileSync(FILE_TO_STORE_LOGIN, 'utf8');
335
+ const lastpath = path.basename(FOLDER_TO_UPLOAD);
336
+
337
+ let meta={"id":data,"folder":lastpath,"uploads":uploads,"is_latest":true}
338
+
339
+
340
+ let docalreadyexists=await manifestExists(data,lastpath,client)
341
+ if (docalreadyexists){
342
+ await deleteManifest(data,lastpath,client)
343
+ }
344
+
345
+ await coll.findOneAndUpdate(
346
+ {"owner":"system"},
347
+ {
348
+ $push: {
349
+ manifests: meta
350
+ }})
351
+ }
352
+ catch(e){
353
+
354
+ console.log(e)
355
+
356
+ }
357
+
358
+ }
359
+
360
+
361
+ yargs(hideBin(process.argv))
362
+ .command(
363
+ 'op',
364
+ 'Interface for ihub operations (login, push, clone).',
365
+ (yargs) => {
366
+ return yargs
367
+ .command(
368
+ 'login <walletpublickey>',
369
+ 'Logs into ihub using a wallet address.',
370
+ (yargs) => {
371
+ return yargs.positional('walletpublickey', {
372
+ describe: 'The wallet address to login with',
373
+ type: 'string'
374
+ });
375
+ },
376
+ (argv) => {
377
+ setUp(argv.walletpublickey);
378
+ console.log("Login successful.");
379
+ }
380
+ )
381
+ .command(
382
+ 'push <folderpath>',
383
+ 'Pushes the contents of a local folder.',
384
+ (yargs) => {
385
+ return yargs.positional('folderpath', {
386
+ describe: 'The local folder to push',
387
+ type: 'string'
388
+ });
389
+ },
390
+ async (argv) => {
391
+
392
+ const { pinata, client } = await getRuntime();
393
+ try{
394
+
395
+
396
+ console.log(`\n⬆️ Starting PUSH operation on folder: ${argv.folderpath}`);
397
+
398
+ gitBundler(argv.folderpath);
399
+ await Push(argv.folderpath,pinata,client);
400
+ console.log('Push operation finished.');
401
+
402
+ }catch(e){
403
+ console.log(str(e))
404
+ }finally{
405
+ await client.close()
406
+ }
407
+
408
+
409
+ }
410
+ )
411
+ .command(
412
+ 'clone <foldername>',
413
+ 'downloaded the repo, the foldername should not be full path but last base path like if we have C:\\Users\gooz\\OneDrive\\Desktop\\ihub , last base path here is ihub',
414
+ (yargs) => {
415
+ return yargs
416
+ .positional('foldername', {
417
+ describe: 'The folder to clone',
418
+ type: 'string'
419
+ })
420
+ .option('new', {
421
+ alias: 'n',
422
+ type: 'boolean',
423
+ description: 'new repo or existing repo',
424
+ default: false
425
+ });
426
+ },
427
+ async (argv) => {
428
+
429
+ const { pinata, client } = await getRuntime();
430
+ try {
431
+ console.log(`\n⬇️ Starting CLONE operation`);
432
+ if(argv.new==true){
433
+ await CloneNew(argv.foldername,pinata,client)
434
+
435
+ }
436
+ else {
437
+ await Clone(argv.foldername,pinata,client);
438
+ console.log('Clone operation finished.');
439
+
440
+ }
441
+ }
442
+ catch(e){
443
+ console.log(String(e))
444
+ }
445
+ finally{
446
+ await client.close()
447
+ }
448
+
449
+ }
450
+ )
451
+ .demandCommand(1, 'You must provide a subcommand for "ihub" (login, push, or clone).')
452
+ .help()
453
+ }
454
+ )
455
+ .demandCommand(1, 'You must provide a top-level command like "ihub".')
456
+ .help()
457
+ .argv;
458
+
459
+
460
+
461
+
462
+
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "ihub-cli",
3
+ "version": "1.0.0",
4
+ "description": "immutablehub cli to push and clone repos",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
9
+ },
10
+ "bin": {
11
+ "ihub": "./index.js"
12
+ },
13
+ "author": "immutablehub",
14
+ "license": "ISC",
15
+ "dependencies": {
16
+ "jest": "^30.2.0",
17
+ "mongodb": "^7.0.0",
18
+ "pinata": "^2.5.1",
19
+ "yargs": "^18.0.0"
20
+ }
21
+ }
@@ -0,0 +1,80 @@
1
+ import fs from "fs"
2
+ import path from "path";
3
+ /**
4
+ * Get all files from a directory recursively
5
+ * @param {string} dir - Directory path
6
+ * @param {string[]} fileList - Accumulator for file paths
7
+ * @returns {string[]} Array of file paths
8
+ */
9
+ function getAllFiles(dir, fileList = []) {
10
+
11
+
12
+ const skipDirs = ['node_modules', '.git', 'dist', 'build', '.next', 'coverage'];
13
+ const files = fs.readdirSync(dir);
14
+
15
+ files.forEach(file => {
16
+
17
+ if (skipDirs.includes(file)) return;
18
+ const filePath = path.join(dir, file);
19
+ if (fs.statSync(filePath).isDirectory()) {
20
+ getAllFiles(filePath, fileList);
21
+ } else {
22
+ fileList.push(filePath);
23
+ }
24
+ });
25
+
26
+ return fileList;
27
+ }
28
+
29
+
30
+ /**
31
+ * Get MIME type from file extension
32
+ * @param {string} fileName - File name
33
+ * @returns {string} MIME type
34
+ */
35
+ function getMimeType(fileName) {
36
+ const ext = path.extname(fileName).toLowerCase();
37
+ const types = {
38
+ '.txt': 'text/plain',
39
+ '.html': 'text/html',
40
+ '.css': 'text/css',
41
+ '.js': 'text/javascript',
42
+ '.json': 'application/json',
43
+ '.png': 'image/png',
44
+ '.jpg': 'image/jpeg',
45
+ '.jpeg': 'image/jpeg',
46
+ '.gif': 'image/gif',
47
+ '.svg': 'image/svg+xml',
48
+ '.pdf': 'application/pdf',
49
+ '.zip': 'application/zip',
50
+ '.mp4': 'video/mp4',
51
+ '.mp3': 'audio/mpeg',
52
+ '.webp': 'image/webp',
53
+ '.ico': 'image/x-icon',
54
+ '.xml': 'application/xml',
55
+ '.csv': 'text/csv',
56
+ '.md': 'text/markdown',
57
+ };
58
+ return types[ext] || 'application/octet-stream';
59
+ }
60
+
61
+
62
+
63
+ /**
64
+ * Convert file paths to File objects for Pinata
65
+ * @param {string} dirPath - Directory path
66
+ * @returns {File[]} Array of File objects
67
+ */
68
+ export default function dirToFileArray(dirPath) {
69
+ const filePaths = getAllFiles(dirPath);
70
+
71
+ return filePaths.map(filePath => {
72
+ const buffer = fs.readFileSync(filePath);
73
+ const fileName = path.relative(dirPath, filePath);
74
+ const mimeType = getMimeType(fileName);
75
+ return new File([buffer], fileName,{type:mimeType});
76
+ });
77
+ }
78
+ //const FOLDER_TO_UPLOAD = path.resolve('./demofolder');
79
+ //let f=dirToFileArray(FOLDER_TO_UPLOAD)
80
+ //console.log(f)