Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ CHIRPSTACK_HOST=localhost
CONTRACT_LABEL=M3ters
MAINNET_RPC=https://ethereum-sepolia-rpc.publicnode.com #alt https://eth-sepolia.g.alchemy.com/v2/<YOUR_API_KEY>
ETHEREUM_PRIVATE_KEY="..."
ROLLUP_CONTRACT_ADDRESS="0x9a9ED9b9A9BEC10d1B5E41b1873E78d19501D5Fa"

# Streamr Configuration
STREAMR_STREAM_ID="0x567853282663b601bfdb9203819b1fbb3fe18926/m3tering/test"
Expand Down
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
FROM node:20-alpine

# Use Node.js headers bundled with the base image
ENV npm_config_nodedir=/usr/local

# Create working directory
WORKDIR /opt/app

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ A modular, extensible service console for providers on the M3tering protocol. Fe
CONTRACT_LABEL=M3ters
MAINNET_RPC=https://ethereum-sepolia-rpc.publicnode.com #alt https://eth-sepolia.g.alchemy.com/v2/<YOUR_API_KEY>
ETHEREUM_PRIVATE_KEY="..."
ROLLUP_CONTRACT_ADDRESS="0x9a9ED9b9A9BEC10d1B5E41b1873E78d19501D5Fa"

# Streamr Configuration
STREAMR_STREAM_ID="0x567853282663b601bfdb9203819b1fbb3fe18926/m3tering/test"
Expand Down
18 changes: 18 additions & 0 deletions compose.dev.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
services:
console:
build:
context: .
dockerfile: Dockerfile
image: m3tering-console:dev
network_mode: host
restart: unless-stopped
env_file:
- .env
volumes:
- ./.data-dev:/opt/app/.data
- console_dev_src_modules:/opt/app/src/lib
- console_dev_dist_modules:/opt/app/dist/lib

volumes:
console_dev_src_modules:
console_dev_dist_modules:
File renamed without changes.
60 changes: 0 additions & 60 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

103 changes: 29 additions & 74 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,102 +1,57 @@
import "dotenv/config";
import { handleUplinks } from "./services/mqtt";
import { Request, Response } from "express";
import { app, contractsReady } from "./services/context";
import {
loadExtensionsFromConfig,
loadUIExtensionsFromConfig,
getUIComponents,
invokeUIAction,
runHook,
} from "./lib/utils";
import setupDatabase, { getAllMeterRecords, deleteMeterByPublicKey } from "./store/sqlite";

// Async initialization function
async function initializeApp() {
import 'dotenv/config';
import { handleUplinks } from './services/mqtt';
import { contractsReady } from './services/contracts';
import { startServer } from './services/server';
import './services/ssh'; // attaches ssh and websocket to server
import { loadExtensionsFromConfig, loadUIExtensionsFromConfig, runHook } from './lib/utils';
import setupDatabase from './store/sqlite';
import { createLogger } from './utils/logger';

const logger = createLogger('[index]');

(async function () {
try {
console.log("[info] Starting application initialization...");
logger.info('Starting application initialization(v1.0.0)...');

// Load extensions from config
await loadExtensionsFromConfig();
console.log("[info] Extensions loaded successfully");
logger.info('Extensions loaded successfully');

// Load UI extensions
await loadUIExtensionsFromConfig();
console.log("[info] UI extensions loaded successfully");
logger.info('UI extensions loaded successfully');

await contractsReady;
console.log("[info] resolved contract addresses");
await contractsReady();

runHook("onBeforeInit");
runHook('onBeforeInit');

// Initialize database tables and jobs
setupDatabase();

console.log("[info] Database setup completed");
logger.info('Database setup completed');

runHook("onDatabaseSetup");
runHook('onDatabaseSetup');

try {
console.log("[info] Initializing MQTT handling...");
logger.info('Initializing MQTT handling...');

// Start MQTT handling
await handleUplinks();

console.log("[info] MQTT handling initialized successfully");
logger.info('MQTT handling initialized successfully');
} catch (mqttError) {
console.error("[error] MQTT initialization failed:", mqttError);
logger.error('[error] MQTT initialization failed:', mqttError);
throw mqttError;
}

console.log("[info] Application initialization completed successfully");
logger.info('Application initialization completed successfully');

startServer();

runHook("onAfterInit");
runHook('onAfterInit');
} catch (error) {
console.error("[fatal] Failed to initialize application:", error);
runHook("onInitError", error);
logger.error('[fatal] Failed to initialize application:', error);
runHook('onInitError', error);
process.exit(1);
}
}

// Start initialization
initializeApp();

app.get("/", async (req: Request, res: Response) => {
const m3ters = getAllMeterRecords();

// Get UI components from loaded UI extensions
const { icons, windows } = await getUIComponents();

res.render("index", { m3ters, icons, windows });
console.log("[server]: Server handled GET request at `/`");
});

// API endpoint to invoke UI actions
app.post("/api/actions/:moduleId/:actionId", async (req: Request, res: Response) => {
const { moduleId, actionId } = req.params;
console.log(`[server]: Invoking action '${actionId}' from module '${moduleId}'`);

const result = await invokeUIAction(moduleId, actionId);

if (result.success) {
res.status(200).json(result);
} else {
res.status(400).json(result);
}
});

app.delete("/delete-meter", async (req: Request, res: Response) => {
let publicKey = decodeURIComponent(req.query?.publicKey?.toString() as string);
if (publicKey) {
const deleted = deleteMeterByPublicKey(publicKey.toString());
if (!deleted) {
res.status(404).send({ message: "meter not found" });
return;
}
} else {
res.status(400).send({ message: "public key not specified" });
return;
}
console.log("[server]: Server handled DELETE request at `/delete-meter`");
res.status(200).send({ message: "deleted meter" });
});
})();
13 changes: 8 additions & 5 deletions src/lib/core/arweave/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { ArweaveSigner, TurboFactory } from "@ardrive/turbo-sdk";
import { Readable } from "stream";
import Arweave from "arweave";
import type { DecodedPayload, Hooks } from "../../../types";
import { createLogger } from "../../../utils/logger";

const logger = createLogger("[arweave]");

export default class implements Hooks {
async onTransactionDistribution(m3terId: number, decoded: DecodedPayload) {
Expand Down Expand Up @@ -45,20 +48,20 @@ export default class implements Hooks {
},
events: {
onUploadProgress: (progress) => {
console.log("[arweave] Upload progress:", progress);
logger.info("Upload progress:", progress);
},
onError: (error) => {
console.error("[arweave] Upload error:", error);
logger.error("Upload error:", error);
},
onSuccess(event) {
console.log("[arweave] Upload successful! Transaction ID:", event);
logger.info("Upload successful! Transaction ID:", event);
},
onUploadSuccess(event) {
console.log("[arweave] Upload completed! Transaction ID:", event);
logger.info("Upload completed! Transaction ID:", event);
},
},
});

console.log(`[arweave] Uploaded transaction ${decoded.nonce} for M3ter ID ${m3terId} to Arweave.`);
logger.info(`Uploaded transaction ${decoded.nonce} for M3ter ID ${m3terId} to Arweave.`);
}
}
Loading
Loading