bug fixes and updates
This commit is contained in:
parent
47477979ed
commit
4b8725ac06
@ -30,7 +30,7 @@
|
||||
"author": "Debros",
|
||||
"license": "gnu-gpl-v3.0",
|
||||
"dependencies": {
|
||||
"@debros/network": "^0.0.14-alpha",
|
||||
"@debros/network": "^0.0.16-alpha",
|
||||
"@ipshipyard/node-datachannel": "0.26.5",
|
||||
"@solana/web3.js": "^1.87.6",
|
||||
"chalk": "^5.3.0",
|
||||
|
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@ -9,8 +9,8 @@ importers:
|
||||
.:
|
||||
dependencies:
|
||||
'@debros/network':
|
||||
specifier: ^0.0.14-alpha
|
||||
version: 0.0.14-alpha(bufferutil@4.0.9)(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(typescript@5.8.2)(utf-8-validate@5.0.10)
|
||||
specifier: ^0.0.16-alpha
|
||||
version: 0.0.16-alpha(bufferutil@4.0.9)(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(typescript@5.8.2)(utf-8-validate@5.0.10)
|
||||
'@ipshipyard/node-datachannel':
|
||||
specifier: 0.26.5
|
||||
version: 0.26.5
|
||||
@ -782,8 +782,8 @@ packages:
|
||||
'@dabh/diagnostics@2.0.3':
|
||||
resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==}
|
||||
|
||||
'@debros/network@0.0.14-alpha':
|
||||
resolution: {integrity: sha512-dMImNOU3f3PURLPqgY4UZrn7cpr4mGuPsDDrTe/HJZey1aa139R3BPJwCmq6EN/MjGUyL2liTjX+ZQ5qBqTbpg==}
|
||||
'@debros/network@0.0.16-alpha':
|
||||
resolution: {integrity: sha512-QYPUcoRF80tO8vD8cbPRjXIm17JW5Eul5au+XHyg2SXuWA9XRdIG0tc6Nk46rUll4lI0g4mFVuUSEUpMrZ7UWA==}
|
||||
peerDependencies:
|
||||
typescript: '>=5.0.0'
|
||||
|
||||
@ -5798,7 +5798,7 @@ snapshots:
|
||||
enabled: 2.0.0
|
||||
kuler: 2.0.0
|
||||
|
||||
'@debros/network@0.0.14-alpha(bufferutil@4.0.9)(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(typescript@5.8.2)(utf-8-validate@5.0.10)':
|
||||
'@debros/network@0.0.16-alpha(bufferutil@4.0.9)(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(typescript@5.8.2)(utf-8-validate@5.0.10)':
|
||||
dependencies:
|
||||
'@chainsafe/libp2p-gossipsub': 14.1.1
|
||||
'@chainsafe/libp2p-noise': 16.1.0
|
||||
|
@ -3,7 +3,6 @@ import { program } from "commander";
|
||||
import { uploadCommand } from "./commands/upload.js";
|
||||
import { deployCommand } from "./commands/deploy.js";
|
||||
import { listCommand } from "./commands/list.js";
|
||||
import { initCommand } from "./commands/init.js";
|
||||
import { configureIPNSCommand } from "./commands/configure-ipns.js";
|
||||
import { domainCommand } from "./commands/domain.js";
|
||||
import { versionsCommand } from "./commands/versions.js";
|
||||
@ -24,7 +23,6 @@ program
|
||||
uploadCommand(program);
|
||||
deployCommand(program);
|
||||
listCommand(program);
|
||||
initCommand(program);
|
||||
configureIPNSCommand(program);
|
||||
domainCommand(program);
|
||||
versionsCommand(program);
|
||||
|
@ -3,7 +3,7 @@ import fs from 'fs';
|
||||
import { Command } from 'commander';
|
||||
import { buildDockerImage, saveDockerImage } from '../services/docker.js';
|
||||
import { uploadToIPFS, publishNodeList } from '../services/ipfs.js';
|
||||
import { announceDeployment } from '../services/network.js';
|
||||
import { announceDeployment, checkAppNameAvailability } from '../services/network.js';
|
||||
import { logger } from '../utils/logger.js';
|
||||
import { config } from '../utils/config.js';
|
||||
|
||||
@ -30,6 +30,14 @@ export function uploadCommand(program: Command) {
|
||||
const appName = options.name || path.basename(folderPath);
|
||||
const tag = options.tag || 'latest';
|
||||
|
||||
// Check if the app name is available in the network
|
||||
const isAppNameAvailable = await checkAppNameAvailability(appName);
|
||||
if (!isAppNameAvailable) {
|
||||
logger.error(`Application name "${appName}" is already taken in the DeBros network.`);
|
||||
logger.info('Please choose a different name using the --name option.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Get the subdomain from options or use the app name
|
||||
const subdomain = options.domain || appName;
|
||||
|
||||
|
@ -3,6 +3,9 @@ import {
|
||||
getConnectedPeers,
|
||||
initIpfs,
|
||||
stopIpfs,
|
||||
initOrbitDB,
|
||||
openDB,
|
||||
getOrbitDB,
|
||||
} from "@debros/network";
|
||||
import { logger, startSpinner, stopSpinner } from "../utils/logger.js";
|
||||
import { config } from "../utils/config.js";
|
||||
@ -12,6 +15,123 @@ import { NodeInfo } from "./ipfs.js";
|
||||
// It does not manage local node deployment, but rather allows the CLI
|
||||
// to discover, communicate with, and deploy to remote nodes in the network
|
||||
|
||||
interface AppData {
|
||||
appName: string;
|
||||
cid: string;
|
||||
version: string;
|
||||
timestamp: string;
|
||||
deployed: boolean;
|
||||
domain?: string;
|
||||
status: 'running' | 'stopped' | 'unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the application_data OrbitDB instance
|
||||
*/
|
||||
async function getApplicationDataDB() {
|
||||
try {
|
||||
// Initialize IPFS if it's not already running
|
||||
await initIpfs();
|
||||
|
||||
// Initialize OrbitDB
|
||||
await initOrbitDB();
|
||||
|
||||
// Open the application_data database (create if it doesn't exist)
|
||||
// Feed type is suitable for append-only logs like application deployment records
|
||||
const db = await openDB('application_data', 'feed');
|
||||
|
||||
return db;
|
||||
} catch (error) {
|
||||
logger.error(`Failed to initialize application_data database: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an application name is available in the network
|
||||
*/
|
||||
export async function checkAppNameAvailability(appName: string): Promise<boolean> {
|
||||
const spinner = startSpinner(`Checking if application name "${appName}" is available...`);
|
||||
|
||||
try {
|
||||
// Get the application_data database
|
||||
const db = await getApplicationDataDB();
|
||||
|
||||
// Query the database for entries with this app name
|
||||
const entries = await db.iterator({ limit: -1 }).collect();
|
||||
const existingApps = entries
|
||||
.map((entry: any) => entry.value.appName)
|
||||
.filter((name: string) => name === appName);
|
||||
|
||||
const isAvailable = existingApps.length === 0;
|
||||
|
||||
stopSpinner(`Application name "${appName}" is ${isAvailable ? 'available' : 'already taken'}`, true);
|
||||
|
||||
// Clean up IPFS
|
||||
await stopIpfs();
|
||||
|
||||
return isAvailable;
|
||||
} catch (error) {
|
||||
stopSpinner(
|
||||
`Failed to check application name availability: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
false
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store application data in OrbitDB
|
||||
*/
|
||||
export async function storeAppData(appData: AppData): Promise<boolean> {
|
||||
const spinner = startSpinner(`Storing application data for ${appData.appName}...`);
|
||||
|
||||
try {
|
||||
// Get the application_data database
|
||||
const db = await getApplicationDataDB();
|
||||
|
||||
// Add the application data to the database
|
||||
const hash = await db.add(appData);
|
||||
logger.debug(`Stored app data with hash: ${hash}`);
|
||||
|
||||
// Create a message to broadcast the app data update to peers
|
||||
const message = {
|
||||
type: "app-data-update",
|
||||
appData,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
// Get the libp2p instance
|
||||
const libp2p = getLibp2p();
|
||||
if (libp2p) {
|
||||
// Publish to the debros-app-data topic
|
||||
await libp2p.services.pubsub.publish(
|
||||
"debros-app-data",
|
||||
new TextEncoder().encode(JSON.stringify(message))
|
||||
);
|
||||
}
|
||||
|
||||
stopSpinner(`Application data for ${appData.appName} stored successfully`, true);
|
||||
|
||||
// Clean up IPFS
|
||||
await stopIpfs();
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
stopSpinner(
|
||||
`Failed to store application data: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
false
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all currently connected peers
|
||||
*/
|
||||
@ -119,6 +239,17 @@ export async function announceDeployment(
|
||||
"debros-deploy",
|
||||
new TextEncoder().encode(JSON.stringify(message))
|
||||
);
|
||||
|
||||
// Update app data in OrbitDB
|
||||
await storeAppData({
|
||||
appName,
|
||||
cid,
|
||||
version,
|
||||
timestamp: new Date().toISOString(),
|
||||
deployed: true,
|
||||
domain: appDomain,
|
||||
status: 'running'
|
||||
});
|
||||
|
||||
stopSpinner(`Deployment announced to the network`, true);
|
||||
|
||||
@ -202,6 +333,53 @@ export async function listenForDeployments(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all applications and their data
|
||||
*/
|
||||
export async function getAllApps(): Promise<AppData[]> {
|
||||
const spinner = startSpinner("Fetching all applications...");
|
||||
|
||||
try {
|
||||
// Get the application_data database
|
||||
const db = await getApplicationDataDB();
|
||||
|
||||
// Query all entries from the database
|
||||
const entries = await db.iterator({ limit: -1 }).collect();
|
||||
|
||||
// Process the entries to get the latest status for each app
|
||||
const appMap = new Map<string, AppData>();
|
||||
|
||||
// Process entries in reverse (newest first) to get the latest status
|
||||
entries.reverse().forEach((entry: any) => {
|
||||
const appData = entry.value as AppData;
|
||||
|
||||
// If we haven't seen this app before, or if this is a newer entry, update our record
|
||||
if (!appMap.has(appData.appName) ||
|
||||
new Date(appMap.get(appData.appName)!.timestamp) < new Date(appData.timestamp)) {
|
||||
appMap.set(appData.appName, appData);
|
||||
}
|
||||
});
|
||||
|
||||
// Convert the map values to an array
|
||||
const apps = Array.from(appMap.values());
|
||||
|
||||
stopSpinner(`Found ${apps.length} applications`, true);
|
||||
|
||||
// Clean up IPFS
|
||||
await stopIpfs();
|
||||
|
||||
return apps;
|
||||
} catch (error) {
|
||||
stopSpinner(
|
||||
`Failed to fetch applications: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
false
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get versions of an application
|
||||
*/
|
||||
@ -216,41 +394,39 @@ export async function getAppVersions(appName: string): Promise<
|
||||
const spinner = startSpinner(`Fetching versions for ${appName}...`);
|
||||
|
||||
try {
|
||||
// Initialize IPFS if it's not already running
|
||||
await initIpfs();
|
||||
|
||||
// Get the libp2p instance
|
||||
const libp2p = getLibp2p();
|
||||
if (!libp2p) {
|
||||
throw new Error("LibP2P is not initialized");
|
||||
// Get the application_data database
|
||||
const db = await getApplicationDataDB();
|
||||
|
||||
// Query all entries from the database for this app
|
||||
const entries = await db.iterator({ limit: -1 }).collect();
|
||||
|
||||
// Filter entries for this app
|
||||
const appEntries = entries
|
||||
.filter((entry: any) => entry.value.appName === appName)
|
||||
.map((entry: any) => {
|
||||
const data = entry.value as AppData;
|
||||
return {
|
||||
tag: data.version,
|
||||
cid: data.cid,
|
||||
timestamp: data.timestamp,
|
||||
deployed: data.deployed,
|
||||
};
|
||||
});
|
||||
|
||||
// Sort by timestamp, newest first
|
||||
const versions = appEntries.sort((a, b) =>
|
||||
new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
|
||||
);
|
||||
|
||||
// Mark only the latest version as deployed if any are deployed
|
||||
const latestDeployedIndex = versions.findIndex(v => v.deployed);
|
||||
if (latestDeployedIndex !== -1) {
|
||||
// Set all versions to not deployed
|
||||
versions.forEach(v => v.deployed = false);
|
||||
// Set only the latest to deployed
|
||||
versions[latestDeployedIndex].deployed = true;
|
||||
}
|
||||
|
||||
// Get application versions from network
|
||||
// In a real implementation, this would query OrbitDB or another storage mechanism
|
||||
|
||||
// Simulate fetching version info from network
|
||||
// This would typically come from querying the network or local database
|
||||
const versions = [
|
||||
{
|
||||
tag: "latest",
|
||||
cid: "QmVersion1",
|
||||
timestamp: new Date().toISOString(),
|
||||
deployed: true,
|
||||
},
|
||||
{
|
||||
tag: "v1.0.0",
|
||||
cid: "QmVersion2",
|
||||
timestamp: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(), // 1 day ago
|
||||
deployed: false,
|
||||
},
|
||||
{
|
||||
tag: "v0.9.0",
|
||||
cid: "QmVersion3",
|
||||
timestamp: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(), // 1 week ago
|
||||
deployed: false,
|
||||
},
|
||||
];
|
||||
|
||||
stopSpinner(`Found ${versions.length} versions for ${appName}`, true);
|
||||
|
||||
// Clean up IPFS
|
||||
@ -354,6 +530,17 @@ export async function rollbackToVersion(
|
||||
"debros-deploy",
|
||||
new TextEncoder().encode(JSON.stringify(message))
|
||||
);
|
||||
|
||||
// Update app data in OrbitDB
|
||||
await storeAppData({
|
||||
appName,
|
||||
cid: targetVersion.cid,
|
||||
version: targetVersion.tag,
|
||||
timestamp: new Date().toISOString(),
|
||||
deployed: true,
|
||||
domain: appDomain,
|
||||
status: 'running'
|
||||
});
|
||||
|
||||
stopSpinner(`Rolled back ${appName} to version ${targetVersion.tag}`, true);
|
||||
|
||||
@ -415,6 +602,18 @@ export async function stopApplication(
|
||||
"debros-app-actions",
|
||||
new TextEncoder().encode(JSON.stringify(message))
|
||||
);
|
||||
|
||||
// Update app data in OrbitDB
|
||||
const apps = await getAllApps();
|
||||
const appData = apps.find(app => app.appName === appName);
|
||||
|
||||
if (appData) {
|
||||
await storeAppData({
|
||||
...appData,
|
||||
status: 'stopped',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
|
||||
stopSpinner(
|
||||
`Stop command for ${appName} has been sent to the network`,
|
||||
@ -493,6 +692,21 @@ export async function startApplication(
|
||||
"debros-app-actions",
|
||||
new TextEncoder().encode(JSON.stringify(message))
|
||||
);
|
||||
|
||||
// Update app data in OrbitDB
|
||||
const apps = await getAllApps();
|
||||
const appData = apps.find(app => app.appName === appName);
|
||||
const appDomain = appData?.domain || `${appName}.${config.defaultDomain}`;
|
||||
|
||||
await storeAppData({
|
||||
appName,
|
||||
cid: targetVersion.cid,
|
||||
version: versionTag,
|
||||
timestamp: new Date().toISOString(),
|
||||
deployed: true,
|
||||
domain: appDomain,
|
||||
status: 'running'
|
||||
});
|
||||
|
||||
stopSpinner(
|
||||
`Start command for ${appName} with version ${versionTag} has been sent to the network`,
|
||||
@ -553,6 +767,15 @@ export async function deleteApplication(
|
||||
"debros-app-actions",
|
||||
new TextEncoder().encode(JSON.stringify(message))
|
||||
);
|
||||
|
||||
// Update app data in OrbitDB - in a real implementation, we would remove the app data
|
||||
const apps = await getAllApps();
|
||||
const appData = apps.find(app => app.appName === appName);
|
||||
|
||||
if (appData) {
|
||||
// In a real implementation, this would delete the app data from OrbitDB
|
||||
logger.debug(`Deleting app data for ${appName}`);
|
||||
}
|
||||
|
||||
stopSpinner(
|
||||
`Delete command for ${appName} has been sent to the network`,
|
||||
@ -609,4 +832,4 @@ export async function stopListeningForDeployments() {
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
@ -34,7 +34,7 @@ const defaultConfig: DebrosCliConfig = {
|
||||
],
|
||||
defaultDomain: 'debros.sol',
|
||||
solanaEndpoint: 'https://api.mainnet-beta.solana.com',
|
||||
defaultDeploymentStrategy: 'k3s'
|
||||
defaultDeploymentStrategy: 'docker'
|
||||
};
|
||||
|
||||
// Ensure config directory exists
|
||||
|
Loading…
x
Reference in New Issue
Block a user