bug fixes and updates

This commit is contained in:
12inchpenguin 2025-04-06 19:02:22 +03:00
parent 47477979ed
commit 4b8725ac06
6 changed files with 273 additions and 44 deletions

View File

@ -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
View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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
*/
@ -120,6 +240,17 @@ export async function announceDeployment(
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);
// Clean up IPFS
@ -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 application_data database
const db = await getApplicationDataDB();
// Get the libp2p instance
const libp2p = getLibp2p();
if (!libp2p) {
throw new Error("LibP2P is not initialized");
// 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
@ -355,6 +531,17 @@ export async function rollbackToVersion(
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);
// Clean up IPFS
@ -416,6 +603,18 @@ export async function stopApplication(
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`,
true
@ -494,6 +693,21 @@ export async function startApplication(
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`,
true
@ -554,6 +768,15 @@ export async function deleteApplication(
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`,
true

View File

@ -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