Add meta update cli tool.
This commit is contained in:
27
apps/cic-meta/src/custom.ts
Normal file
27
apps/cic-meta/src/custom.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import {Addressable, mergeKey, Syncable} from "@cicnet/crdt-meta";
|
||||
|
||||
class Custom extends Syncable implements Addressable {
|
||||
|
||||
name: string
|
||||
value: Object
|
||||
|
||||
constructor(name:string, v:Object={}) {
|
||||
super('', v);
|
||||
Custom.toKey(name).then((cid) => {
|
||||
this.id = cid;
|
||||
this.value = v;
|
||||
});
|
||||
}
|
||||
|
||||
public static async toKey(item:string, identifier: string = ':cic.custom') {
|
||||
return await mergeKey(Buffer.from(item), Buffer.from(identifier));
|
||||
}
|
||||
|
||||
public key(): string {
|
||||
return this.id;
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
Custom,
|
||||
}
|
||||
90
apps/cic-meta/src/db.ts
Normal file
90
apps/cic-meta/src/db.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import * as pg from 'pg';
|
||||
import * as sqlite from 'sqlite3';
|
||||
|
||||
type DbConfig = {
|
||||
name: string
|
||||
host: string
|
||||
port: number
|
||||
user: string
|
||||
password: string
|
||||
}
|
||||
|
||||
interface DbAdapter {
|
||||
query: (s:string, callback:(e:any, rs:any) => void) => void
|
||||
close: () => void
|
||||
}
|
||||
|
||||
const re_creatematch = /^(CREATE)/i
|
||||
const re_getmatch = /^(SELECT)/i;
|
||||
const re_setmatch = /^(INSERT|UPDATE)/i;
|
||||
|
||||
class SqliteAdapter implements DbAdapter {
|
||||
|
||||
db: any
|
||||
|
||||
constructor(dbConfig:DbConfig, callback?:(any) => void) {
|
||||
this.db = new sqlite.Database(dbConfig.name); //, callback);
|
||||
}
|
||||
|
||||
public query(s:string, callback:(e:any, rs?:any) => void): void {
|
||||
const local_callback = (e, rs) => {
|
||||
let r = undefined;
|
||||
if (rs !== undefined) {
|
||||
r = {
|
||||
rowCount: rs.length,
|
||||
rows: rs,
|
||||
}
|
||||
}
|
||||
callback(e, r);
|
||||
};
|
||||
if (s.match(re_getmatch)) {
|
||||
this.db.all(s, local_callback);
|
||||
} else if (s.match(re_setmatch)) {
|
||||
this.db.run(s, local_callback);
|
||||
} else if (s.match(re_creatematch)) {
|
||||
this.db.run(s, callback);
|
||||
} else {
|
||||
throw 'unhandled query';
|
||||
}
|
||||
}
|
||||
|
||||
public close() {
|
||||
this.db.close();
|
||||
}
|
||||
}
|
||||
|
||||
class PostgresAdapter implements DbAdapter {
|
||||
|
||||
db: any
|
||||
|
||||
constructor(dbConfig:DbConfig) {
|
||||
let o = dbConfig;
|
||||
o['database'] = o.name;
|
||||
this.db = new pg.Pool(o);
|
||||
return this.db;
|
||||
}
|
||||
|
||||
public query(s:string, callback:(e:any, rs:any) => void): void {
|
||||
this.db.query(s, (e, rs) => {
|
||||
let r = {
|
||||
length: rs.rowCount,
|
||||
}
|
||||
rs.length = rs.rowCount;
|
||||
if (e === undefined) {
|
||||
e = null;
|
||||
}
|
||||
console.debug(e, rs);
|
||||
callback(e, rs);
|
||||
});
|
||||
}
|
||||
|
||||
public close() {
|
||||
this.db.end();
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
DbConfig,
|
||||
SqliteAdapter,
|
||||
PostgresAdapter,
|
||||
}
|
||||
@@ -1,2 +1,4 @@
|
||||
export { User } from './user';
|
||||
export { Phone } from './phone';
|
||||
export { Custom } from './custom';
|
||||
export { Meta } from './meta';
|
||||
|
||||
126
apps/cic-meta/src/meta.ts
Normal file
126
apps/cic-meta/src/meta.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
import {ArgPair, Envelope, Syncable, MutablePgpKeyStore, PGPSigner} from "@cicnet/crdt-meta";
|
||||
import {User} from "./user";
|
||||
import {Phone} from "./phone";
|
||||
import {Custom} from "./custom";
|
||||
const fetch = require("node-fetch");
|
||||
|
||||
const headers = {
|
||||
'Content-Type': 'application/json;charset=utf-8',
|
||||
'x-cic-automerge': 'client'
|
||||
};
|
||||
const options = {
|
||||
headers: headers,
|
||||
};
|
||||
|
||||
class Meta {
|
||||
keystore: MutablePgpKeyStore = new MutablePgpKeyStore();
|
||||
signer: PGPSigner = new PGPSigner(this.keystore);
|
||||
metaUrl: string;
|
||||
private privateKey: string;
|
||||
onload: (status: boolean) => void;
|
||||
|
||||
constructor(metaUrl: string, privateKey: any) {
|
||||
this.metaUrl = metaUrl;
|
||||
this.privateKey = privateKey;
|
||||
this.keystore.loadKeyring().then(() => {
|
||||
this.keystore.importPrivateKey(privateKey).then(() => this.onload(true));
|
||||
});
|
||||
}
|
||||
|
||||
async set(identifier: string, data: Object): Promise<any> {
|
||||
let syncable: Syncable;
|
||||
const response = await Meta.get(identifier, this.metaUrl);
|
||||
if (response === `Request to ${this.metaUrl}/${identifier} failed. Connection error.`) {
|
||||
return response;
|
||||
} else if (typeof response !== "object" || typeof data !== "object") {
|
||||
syncable = new Syncable(identifier, data);
|
||||
const res = await this.updateMeta(syncable, identifier);
|
||||
return `${res.status}: ${res.statusText}`;
|
||||
} else {
|
||||
syncable = await Meta.get(identifier, this.metaUrl);
|
||||
let update: Array<ArgPair> = [];
|
||||
for (const prop in data) {
|
||||
update.push(new ArgPair(prop, data[prop]));
|
||||
}
|
||||
syncable.update(update, 'client-branch');
|
||||
const res = await this.updateMeta(syncable, identifier);
|
||||
return `${res.status}: ${res.statusText}`;
|
||||
}
|
||||
}
|
||||
|
||||
async updateMeta(syncable: Syncable, identifier: string): Promise<any> {
|
||||
const envelope: Envelope = await this.wrap(syncable);
|
||||
const reqBody: string = envelope.toJSON();
|
||||
const putOptions = {
|
||||
method: 'PUT',
|
||||
headers: headers,
|
||||
body: reqBody
|
||||
};
|
||||
return await fetch(`${this.metaUrl}/${identifier}`, putOptions).then(async response => {
|
||||
if (response.ok) {
|
||||
return Promise.resolve({
|
||||
status: response.status,
|
||||
statusText: response.statusText + ', Metadata updated successfully!'
|
||||
});
|
||||
} else {
|
||||
return Promise.reject({
|
||||
status: response.status,
|
||||
statusText: response.statusText
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static async get(identifier: string, metaUrl: string): Promise<any> {
|
||||
const response = await fetch(`${metaUrl}/${identifier}`, options).then(response => {
|
||||
if (response.ok) {
|
||||
return (response.json());
|
||||
} else {
|
||||
return Promise.reject({
|
||||
status: response.status,
|
||||
statusText: response.statusText
|
||||
});
|
||||
}
|
||||
}).catch(error => {
|
||||
if (error.code === 'ECONNREFUSED') {
|
||||
return `Request to ${metaUrl}/${identifier} failed. Connection error.`
|
||||
}
|
||||
return `${error.status}: ${error.statusText}`;
|
||||
});
|
||||
if (typeof response !== "object") {
|
||||
return response;
|
||||
}
|
||||
return Envelope.fromJSON(JSON.stringify(response)).unwrap();
|
||||
}
|
||||
|
||||
static async getIdentifier(name: string, type: string = 'custom'): Promise<string> {
|
||||
let identifier: string;
|
||||
type = type.toLowerCase();
|
||||
if (type === 'user') {
|
||||
identifier = await User.toKey(name);
|
||||
} else if (type === 'phone') {
|
||||
identifier = await Phone.toKey(name);
|
||||
} else {
|
||||
identifier = await Custom.toKey(name);
|
||||
}
|
||||
return identifier;
|
||||
}
|
||||
|
||||
private wrap(syncable: Syncable): Promise<Envelope> {
|
||||
return new Promise<Envelope>(async (resolve, reject) => {
|
||||
syncable.setSigner(this.signer);
|
||||
syncable.onwrap = async (env) => {
|
||||
if (env === undefined) {
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
resolve(env);
|
||||
};
|
||||
syncable.sign();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
Meta,
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Syncable, Addressable, mergeKey } from 'crdt-meta';
|
||||
import { Syncable, Addressable, mergeKey } from '@cicnet/crdt-meta';
|
||||
|
||||
class Phone extends Syncable implements Addressable {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Syncable, Addressable, toAddressKey } from 'crdt-meta';
|
||||
import { Syncable, Addressable, toAddressKey } from '@cicnet/crdt-meta';
|
||||
|
||||
const keySalt = new TextEncoder().encode(':cic.person');
|
||||
class User extends Syncable implements Addressable {
|
||||
|
||||
Reference in New Issue
Block a user