nodejs: addon

This commit is contained in:
Avently
2024-12-12 22:11:42 +07:00
parent 708368c9c5
commit a5e7fa2f9f
6 changed files with 291 additions and 0 deletions
+3
View File
@@ -79,3 +79,6 @@ website/package-lock.json
website/.cache
website/test/stubs-layout-cache/_includes/*.js
apps/android/app/release
packages/simplex-chat-node/build
packages/simplex-chat-node/.vscode
packages/simplex-chat-node/libs
+13
View File
@@ -0,0 +1,13 @@
{
"targets": [
{
"target_name": "addon",
"sources": [ "cpp/simplex.cc" ],
"libraries": [
"-L<(module_root_dir)/libs",
"-lHSsimplex-chat-6.2.0.5-inplace-ghc9.6.3",
"-L<(module_root_dir)/build/Release/"
]
}
]
}
+188
View File
@@ -0,0 +1,188 @@
#include <sstream>
#include <string>
#include <node.h>
#include "simplex.h"
namespace simplex
{
using v8::Array;
using v8::ArrayBuffer;
using v8::Context;
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Maybe;
using v8::Number;
using v8::Object;
using v8::String;
using v8::Value;
void haskell_init()
{
int argc = 5;
char *argv[] = {
"simplex",
"+RTS", // requires `hs_init_with_rtsopts`
"-A64m", // chunk size for new allocations
"-H64m", // initial heap size
"-xn", // non-moving GC
0};
char **pargv = argv;
hs_init_with_rtsopts(&argc, &pargv);
}
void ChatMigrateInit(const FunctionCallbackInfo<Value> &args)
{
Isolate *isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
std::string path(*(v8::String::Utf8Value(isolate, args[0])));
std::string key(*(v8::String::Utf8Value(isolate, args[1])));
std::string confirm(*(v8::String::Utf8Value(isolate, args[2])));
long *ctrl(0);
std::string res = chat_migrate_init(path.c_str(), key.c_str(), confirm.c_str(), &ctrl);
std::stringstream ss;
ss << ctrl
<< "\n"
<< res;
// printf("ChatCtrl after init %d", cChatCtrl);
// v8::Local<v8::String> ctrl = v8::String::NewFromUtf8(isolate, cppChatCtrl.c_str(), v8::String::kNormalString);
args.GetReturnValue().Set(
String::NewFromUtf8(
isolate, ss.str().c_str())
.ToLocalChecked());
}
void ChatCloseStore(const FunctionCallbackInfo<Value> &args)
{
Isolate *isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
long *ctrl = (long *)((long)args[0].As<Number>()->Value());
args.GetReturnValue().Set(
String::NewFromUtf8(
isolate, chat_close_store(ctrl))
.ToLocalChecked());
}
void ChatSendCmd(const FunctionCallbackInfo<Value> &args)
{
Isolate *isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
long *ctrl = (long *)((long)args[0].As<Number>()->Value());
std::string cmd(*(v8::String::Utf8Value(isolate, args[1])));
args.GetReturnValue().Set(
String::NewFromUtf8(
isolate, chat_send_cmd(ctrl, cmd.c_str()))
.ToLocalChecked());
}
void ChatRecvMsgWait(const FunctionCallbackInfo<Value> &args)
{
Isolate *isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
long *ctrl = (long *)((long)args[0].As<Number>()->Value());
long wait = ((long)args[1].As<Number>()->Value());
args.GetReturnValue().Set(
String::NewFromUtf8(
isolate, chat_recv_msg_wait(ctrl, wait))
.ToLocalChecked());
}
void ChatWriteFile(const FunctionCallbackInfo<Value> &args)
{
Isolate *isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
long *ctrl = (long *)((long)args[0].As<Number>()->Value());
std::string path(*(v8::String::Utf8Value(isolate, args[1])));
Local<v8::ArrayBuffer> arr = args[2].As<ArrayBuffer>();
char *buffer = (char *)arr->Data();
int len(arr->ByteLength());
// std::array address(*arr);
// v8::Array a = *buffer;
// std::string data(*((isolate, args[1])));
args.GetReturnValue().Set(
String::NewFromUtf8(
isolate, chat_write_file(ctrl, path.c_str(), buffer, len))
.ToLocalChecked());
}
void ChatReadFile(const FunctionCallbackInfo<Value> &args)
{
Isolate *isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
long *ctrl = (long *)((long)args[0].As<Number>()->Value());
std::string path(*(v8::String::Utf8Value(isolate, args[1])));
Local<v8::ArrayBuffer> arr = args[2].As<ArrayBuffer>();
char *buffer = (char *)arr->Data();
int len(arr->ByteLength());
args.GetReturnValue().Set(
String::NewFromUtf8(
isolate, chat_write_file(ctrl, path.c_str(), buffer, len))
.ToLocalChecked());
}
void ChatEncryptFile(const FunctionCallbackInfo<Value> &args)
{
Isolate *isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
long *ctrl = (long *)((long)args[0].As<Number>()->Value());
std::string fromPath(*(v8::String::Utf8Value(isolate, args[1])));
std::string toPath(*(v8::String::Utf8Value(isolate, args[2])));
args.GetReturnValue().Set(
String::NewFromUtf8(
isolate, chat_encrypt_file(ctrl, fromPath.c_str(), toPath.c_str()))
.ToLocalChecked());
}
void ChatDecryptFile(const FunctionCallbackInfo<Value> &args)
{
Isolate *isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
std::string fromPath(*(v8::String::Utf8Value(isolate, args[0])));
std::string key(*(v8::String::Utf8Value(isolate, args[1])));
std::string nonce(*(v8::String::Utf8Value(isolate, args[2])));
std::string toPath(*(v8::String::Utf8Value(isolate, args[3])));
args.GetReturnValue().Set(
String::NewFromUtf8(
isolate, chat_decrypt_file(fromPath.c_str(), key.c_str(), nonce.c_str(), toPath.c_str()))
.ToLocalChecked());
}
void Initialize(Local<Object> exports)
{
haskell_init();
NODE_SET_METHOD(exports, "chat_migrate_init", ChatMigrateInit);
NODE_SET_METHOD(exports, "chat_close_store", ChatCloseStore);
NODE_SET_METHOD(exports, "chat_send_cmd", ChatSendCmd);
NODE_SET_METHOD(exports, "chat_recv_msg_wait", ChatRecvMsgWait);
NODE_SET_METHOD(exports, "chat_write_file", ChatWriteFile);
NODE_SET_METHOD(exports, "chat_read_file", ChatReadFile);
NODE_SET_METHOD(exports, "chat_encrypt_file", ChatEncryptFile);
NODE_SET_METHOD(exports, "chat_decrypt_file", ChatDecryptFile);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
} // namespace simplex
+46
View File
@@ -0,0 +1,46 @@
//
// simplex.h
// SimpleX
//
// Created by Evgeny on 30/05/2022.
// Copyright © 2022 SimpleX Chat. All rights reserved.
//
#ifndef SimpleX_h
#define SimpleX_h
extern "C" void hs_init(int argc, char **argv[]);
extern "C" void hs_init_with_rtsopts(int * argc, char **argv[]);
typedef long* chat_ctrl;
// the last parameter is used to return the pointer to chat controller
extern "C" char *chat_migrate_init(const char *path, const char *key, const char *confirm, chat_ctrl *ctrl);
extern "C" char *chat_close_store(chat_ctrl ctrl);
extern "C" char *chat_reopen_store(chat_ctrl ctrl);
extern "C" char *chat_send_cmd(chat_ctrl ctrl, const char *cmd);
extern "C" char *chat_recv_msg_wait(chat_ctrl ctrl, const int wait);
extern "C" char *chat_parse_markdown(const char *str);
extern "C" char *chat_parse_server(const char *str);
extern "C" char *chat_password_hash(const char *pwd, const char *salt);
extern "C" char *chat_valid_name(const char *name);
extern "C" int chat_json_length(const char *str);
extern "C" char *chat_encrypt_media(chat_ctrl ctrl, const char *key, const char *frame, const int len);
extern "C" char *chat_decrypt_media(const char *key, const char *frame, const int len);
// chat_write_file returns null-terminated string with JSON of WriteFileResult
extern "C" char *chat_write_file(chat_ctrl ctrl, const char *path, const char *data, const int len);
// chat_read_file returns a buffer with:
// result status (1 byte), then if
// status == 0 (success): buffer length (uint32, 4 bytes), buffer of specified length.
// status == 1 (error): null-terminated error message string.
extern "C" char *chat_read_file(const char *path, const char *key, const char *nonce);
// chat_encrypt_file returns null-terminated string with JSON of WriteFileResult
extern "C" char *chat_encrypt_file(chat_ctrl ctrl, const char *fromPath, const char *toPath);
// chat_decrypt_file returns null-terminated string with the error message
extern "C" char *chat_decrypt_file(const char *fromPath, const char *key, const char *nonce, const char *toPath);
#endif /* simplex_h */
+19
View File
@@ -0,0 +1,19 @@
{
"name": "simplexjs",
"version": "1.0.0",
"main": "src/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/simplex-chat/simplexjs.git"
},
"author": "SimpleX team",
"license": "ISC",
"bugs": {
"url": "https://github.com/simplex-chat/simplexjs/issues"
},
"homepage": "https://github.com/simplex-chat/simplexjs#readme",
"description": ""
}
+22
View File
@@ -0,0 +1,22 @@
const addon = require('../build/Release/addon');
const fs = require('fs');
const ctrlAndRes = addon.chat_migrate_init("db", "a", "yesUp")
const ctrl = Number(ctrlAndRes.split("\n")[0])
const res = ctrlAndRes.split("\n")[1]
console.log("Migrate ctrl:", ctrl, "res:", res)
console.log(addon.chat_send_cmd(ctrl, "/v"))
// const wait = 15_000_000
// console.log(ctrl, addon.chat_recv_msg_wait(ctrl, wait))
const path = "/tmp/write_file.txt"
const data = [0, 1, 2]
console.log(addon.chat_write_file(ctrl, path, new ArrayBuffer(data)))
fs.writeFileSync("/tmp/file_unencrypted.txt", "unencrypted")
const encRes = JSON.parse(addon.chat_encrypt_file(ctrl, "/tmp/file_unencrypted.txt", "/tmp/file_encrypted.txt"))
const key = encRes.cryptoArgs.fileKey
const nonce = encRes.cryptoArgs.fileNonce
console.log(encRes)
console.log(addon.chat_decrypt_file("/tmp/file_encrypted.txt", key, nonce, "/tmp/file_decrypted.txt"))