Mostly working on search and create and changing location of Database file.

New key bindings!
Added ability to search by tag and by date. Though there is still a bug in by date.
Lots of DB code updates.
Adding electron if casses. Some of these features are electrong spesific so it is checking that it is electron.
Changing how tagging control works so I could have multiple on the same page.
Added some error tracking code so I would get notified of internal errors if online.
New preload script (special feature in electron)
This commit is contained in:
Jason Tudisco 2022-02-14 04:56:21 -06:00
parent a76c3491d2
commit 4d57642db4
11 changed files with 3942 additions and 795 deletions

44
main.js
View File

@ -1,28 +1,38 @@
const { app, BrowserWindow } = require('electron')
require('./src/data/sqlite-electron-ipc');
const path = require('path')
const here = app.getAppPath()
console.log('here', here)
let mainWin;
require('./src/data/sqlite-electron-ipc')
// Preload
const Sentry = require('@sentry/browser')
Sentry.init({
dsn: 'http://a12d5c5f800b406f8d1c0c5d2ed63a78@216.128.138.128:8000/1'
})
let mainWin
function createWindow () {
mainWin = new BrowserWindow({
autoHideMenuBar: true,
width: 800,
height: 600,
icon: __dirname + '/src/img/chains.png',
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
},
nodeIntegration: true
})
mainWin = new BrowserWindow({
autoHideMenuBar: true,
width: 800,
height: 600,
icon: __dirname + '/src/img/chains.png',
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
preload: path.join(here, 'preload.js')
},
nodeIntegration: true
})
//win.autoHideMenuBar = true;
// win.autoHideMenuBar = true;
mainWin.loadFile('index.html')
mainWin.loadFile('index.html')
}
app.whenReady().then(() => {
createWindow()
createWindow()
})

3384
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -34,7 +34,8 @@
"electron-rebuild": "^3.2.7",
"jest": "^27.4.7",
"less": "^4.1.2",
"parcel": "^2"
"parcel": "^2",
"standard": "^16.0.4"
},
"dependencies": {
"@sentry/browser": "^6.17.6",
@ -43,6 +44,7 @@
"better-sqlite3": "^7.5.0",
"conf": "^10.1.1",
"dayjs": "^1.10.7",
"electron-config": "^2.0.0",
"empty-lite": "^1.2.0",
"es6-interface": "^3.2.1",
"hash.js": "^1.1.7",

22
preload.js Normal file
View File

@ -0,0 +1,22 @@
// Preload
const Sentry = require('@sentry/browser')
// const { BrowserTracing } require( '@sentry/tracing' )
Sentry.init({
dsn: 'http://a12d5c5f800b406f8d1c0c5d2ed63a78@216.128.138.128:8000/1',
// Alternatively, use `process.env.npm_package_version` for a dynamic release version
// if your build tool supports it.
release: 'timechain@1.0.0'
// integrations: [new BrowserTracing()],
// Set tracesSampleRate to 1.0 to capture 100%
// of transactions for performance monitoring.
// We recommend adjusting this value in production
// tracesSampleRate: 1.0
})
window.Sentry = Sentry
window.Timechain_isElectron = true
window.Timechain_dbModule = 'sqlite-client'
window.Timechain_canChooseDbLocation = true

View File

@ -1,146 +1,192 @@
const { ipcMain } = require('electron');
const { ipcMain } = require('electron')
const { app } = require('electron')
const path = require('path')
const fs = require('fs')
// const config = app.getPath('userData');
const {app} = require('electron');
//const config = app.getPath('userData');
const Config = require('electron-config')
const config = new Config()
const {TimeChainDataSqliteRecord,ConnectToDatabase, TimeChainDataSqliteFile, TimeChainDataSqliteTag, TimeChainDataSqliteTagLink} = require('./sqlite');
let DBPath = config.get('database.path')
if (!DBPath) {
DBPathDir = app.getPath('userData')
DBPath = path.join(DBPathDir, '/timechain.db')
config.set('database.path', DBPath)
}
const { TimeChainDataSqliteRecord, ConnectToDatabase, TimeChainDataSqliteFile, TimeChainDataSqliteTag, TimeChainDataSqliteTagLink } = require('./sqlite')
const appPath = app.getPath('userData');
console.log(DBPath)
const DBPath = appPath+"/timechain.db";
console.log(DBPath);
const DB = ConnectToDatabase(DBPath)
const DB = ConnectToDatabase(DBPath);
const DbRecord = new TimeChainDataSqliteRecord()
const DbFile = new TimeChainDataSqliteFile()
const DbTag = new TimeChainDataSqliteTag()
const DbTagLink = new TimeChainDataSqliteTagLink()
const DbRecord = new TimeChainDataSqliteRecord();
const DbFile = new TimeChainDataSqliteFile();
const DbTag = new TimeChainDataSqliteTag();
const DbTagLink = new TimeChainDataSqliteTagLink();
//const configDir = app.getPath('userData');
//const appPath = app.getPath('exe');
// ** Extra IPC Functions
ipcMain.handle('select-sqlite-file', (event, arg) => {
const { dialog } = require('electron')
const files = dialog.showOpenDialog({
filters: [{
name: 'Sqlite File',
extensions: ['db', 'db3']
}]
})
if (!files) return false
return files
})
ipcMain.handle('create-sqlite-file', (event, arg) => {
const { dialog } = require('electron')
const files = dialog.showSaveDialogSync({
filters: [{
name: 'Sqlite File',
extensions: ['db', 'db3']
}]
})
// ** Extra Data
ipcMain.handle('timechain-config-dir', (event,arg) => {
return false;
});
if (!files) return false
return files
})
/**
* Allows the UI to select another database file
*/
ipcMain.handle('timechain-database-open', (event, arg) => {
if (!arg.filename) return false
if (!fs.existsSync(arg.filename)) return false
config.set('database.path', arg.filename)
app.relaunch()
app.quit()
// DB = ConnectToDatabase(arg.filename)
return true
})
/**
* Allows the UI to create a new database instance
*/
ipcMain.handle('timechain-database-create', (event, arg) => {
if (!arg.filename) return false
console.log('database.path', arg.filename)
config.set('database.path', arg.filename)
app.relaunch()
app.quit()
// DB = ConnectToDatabase(arg.filename)
return true
})
// ** RECORD **
console.log("Register timechain-record");
ipcMain.handle('timechain-record', async (event,arg) => {
console.log('Register timechain-record')
ipcMain.handle('timechain-record', async (event, arg) => {
let res = null
let res = null;
switch(arg.func){
case 'add':
res = await DbRecord.add(arg.uuid, arg.timestamp, arg.content, arg.mime, arg.hash);
break;
case 'delete':
res = await DbRecord.delete(arg.uuid);
break;
case 'update':
res = await DbRecord.update(arg.uuid,arg.content,arg.mime,arg.hash);
break;
case 'find':
res = await DbRecord.find(arg.search,arg.sort,arg.limit,arg.offset);
break;
case 'get':
res = await DbRecord.get(arg.uuid);
break;
default:
res = new Error('unknown command');
}
return res;
switch (arg.func) {
case 'add':
res = await DbRecord.add(arg.uuid, arg.timestamp, arg.content, arg.mime, arg.hash)
break
case 'delete':
res = await DbRecord.delete(arg.uuid)
break
case 'update':
res = await DbRecord.update(arg.uuid, arg.content, arg.mime, arg.hash)
break
case 'find':
res = await DbRecord.find(arg.search, arg.sort, arg.limit, arg.offset)
break
case 'get':
res = await DbRecord.get(arg.uuid)
break
default:
res = new Error('unknown command')
}
return res
})
// ** FILE **
ipcMain.handle('timechain-file', async (event, arg) => {
let res = null;
let res = null
switch(arg.func){
case 'add':
res = await DbFile.add(arg.uuid_record,arg.uuid,arg.timestamp,arg.content,arg.mime,arg.hash);
break;
case 'update':
res = await DbFile.update(arg.uuid,arg.timestamp,arg.content,arg.mime,arg.hash);
break;
case 'get-record':
res = await DbFile.getByRecord(arg.uuid_record);
break;
case 'delete-record':
res = await DbFile.deleteRecord(arg.uuid_record);
break;
case 'get':
res = await DbFile.get(arg.uuid);
break;
case 'delete':
res = await DbFile.delete(arg.uuid);
break;
default:
res = new Error('Unknow a command');
}
switch (arg.func) {
case 'add':
res = await DbFile.add(arg.uuid_record, arg.uuid, arg.timestamp, arg.content, arg.mime, arg.hash)
break
case 'update':
res = await DbFile.update(arg.uuid, arg.timestamp, arg.content, arg.mime, arg.hash)
break
case 'get-record':
res = await DbFile.getByRecord(arg.uuid_record)
break
case 'delete-record':
res = await DbFile.deleteRecord(arg.uuid_record)
break
case 'get':
res = await DbFile.get(arg.uuid)
break
case 'delete':
res = await DbFile.delete(arg.uuid)
break
default:
res = new Error('Unknow a command')
}
return res;
});
return res
})
// ** TAG **
ipcMain.handle('timechain-tag', async (event, arg) => {
let res = null
let res = null;
switch (arg.func) {
case 'add':
res = await DbTag.add(arg.tag)
break
case 'delete':
res = await DbTag.delete(arg.tag)
break
case 'has':
res = await DbTag.delete(arg.tag)
break
default:
res = new Error('Command Unknown')
}
switch(arg.func){
case 'add':
res = await DbTag.add(arg.tag);
break;
case 'delete':
res = await DbTag.delete(arg.tag);
break;
case 'has':
res = await DbTag.delete(arg.tag);
break;
default:
res = new Error('Command Unknown');
}
return res;
});
return res
})
// TAG LINK
ipcMain.handle('timechain-taglink', async (event, arg) => {
let res = null
let res = null;
switch (arg.func) {
case 'add':
res = await DbTagLink.add(arg.uuid, arg.tag)
break
case 'delete':
res = await DbTagLink.delete(arg.uuid, arg.tag)
break
case 'delete-tag':
res = await DbTagLink.deleteTag(arg.tag)
break
case 'delete-record':
res = await DbTagLink.deleteRecord(arg.uuid)
break
case 'get-records':
res = await DbTagLink.getRecords(atg.tag)
break
case 'get-tags':
res = await DbTagLink.getTags(atg.uuid)
break
default:
res = Error('Commande not known')
}
switch(arg.func){
case 'add':
res = await DbTagLink.add(arg.uuid,arg.tag)
break;
case 'delete':
res = await DbTagLink.delete(arg.uuid,arg.tag);
break;
case 'delete-tag':
res = await DbTagLink.deleteTag(arg.tag);
break;
case 'delete-record':
res = await DbTagLink.deleteRecord(arg.uuid);
break;
case 'get-records':
res = await DbTagLink.getRecords(atg.tag);
break;
case 'get-tags':
res = await DbTagLink.getTags(atg.uuid);
break;
default:
res = Error("Commande not known");
}
return res;
});
return res
})

View File

@ -1,471 +1,535 @@
const Interface = require("es6-interface");
const {InterfaceRecord,InterfaceFile,InterfaceTag,InterfaceTagLink} = require("./interfaces");
const Interface = require('es6-interface')
const { InterfaceRecord, InterfaceFile, InterfaceTag, InterfaceTagLink } = require('./interfaces')
const Database = require('better-sqlite3')
let db = null;
let db = null
const ConnectToDatabase = (path)=>{
db = require('better-sqlite3')(path, {});
return db;
const ConnectToDatabase = (path) => {
if (db) {
db.close()
db = null
}
db = new Database(path, {})
return db
}
class TimeChainDataSqliteTag extends Interface(InterfaceTag) {
constructor(){
super();
const table = `CREATE TABLE IF NOT EXISTS "tags" (
"tag" TEXT UNIQUE,
"created_at" INTEGER NOT NULL,
"updated_at" INTEGER NOT NULL,
constructor () {
super()
const table = `CREATE TABLE IF NOT EXISTS "tags" (
"tag" TEXT UNIQUE,
"created_at" INTEGER NOT NULL,
"updated_at" INTEGER NOT NULL,
PRIMARY KEY("tag")
);`;
);`
const table_idx = ``;
const table_idx = ''
if(!db){
throw new Error("Database is not connected");
if (!db) {
throw new Error('Database is not connected')
}
db.exec(table + '\n' + table_idx)
this.db = db
}
get table_add () {
if (!this._table_add) {
this._table_add = db.prepare('INSERT INTO tags (tag,created_at,updated_at) VALUES (?,?,?)')
}
return this._table_add
}
get table_delete () {
if (!this._table_delete) {
this._table_delete = db.prepare('DELETE FROM tags WHERE tag = ?')
}
return this._table_delete
}
get table_get () {
if (!this._table_get) {
this._table_get = db.prepare('SELECT * FROM tags WHERE tag = ?')
}
return this._table_get
}
get table_count () {
if (!this._table_count) {
this._table_count = db.prepare('SELECT count(tag) as cnt FROM tags WHERE tag = ?')
}
return this._table_count
}
add (tag) {
return new Promise(resolve => {
this.has(tag).then(cnt => {
if (cnt) {
return resolve(1)
} else {
const dt = Math.floor(Date.now())
const res = this.table_add.run(tag, dt, dt)
return resolve(res?.changes)
}
})
})
}
delete (tag) {
return new Promise(resolve => {
const dt = Math.floor(Date.now())
const res = this.table_delete.run(tag)
return resolve(res?.changes)
})
}
db.exec(table + "\n" + table_idx);
this.db = db;
}
get table_add(){
if(!this._table_add){
this._table_add = db.prepare("INSERT INTO tags (tag,created_at,updated_at) VALUES (?,?,?)");
}
return this._table_add;
}
get table_delete(){
if(!this._table_delete){
this._table_delete = db.prepare("DELETE FROM tags WHERE tag = ?");
}
return this._table_delete;
}
get table_get(){
if(!this._table_get){
this._table_get = db.prepare("SELECT * FROM tags WHERE tag = ?");
}
return this._table_get;
}
get table_count(){
if(!this._table_count){
this._table_count = db.prepare("SELECT count(tag) as cnt FROM tags WHERE tag = ?");
}
return this._table_count;
}
add(tag){
return new Promise(resolve=>{
this.has(tag).then(cnt=>{
if(cnt){
return resolve(1);
}else{
const dt = Math.floor(Date.now());
const res = this.table_add.run(tag,dt,dt);
return resolve(res?.changes);
}
});
});
}
delete(tag){
return new Promise(resolve=>{
const dt = Math.floor(Date.now());
const res = this.table_delete.run(tag);
return resolve(res?.changes);
});
}
get(tag){
return new Promise(resolve=>{
const res = this.table_get.get(tag);
return resolve(res);
});
}
has(tag){
return new Promise(resolve=>{
const res = this.table_count.get(tag);
return resolve(res && res.cnt>0);
});
}
get (tag) {
return new Promise(resolve => {
const res = this.table_get.get(tag)
return resolve(res)
})
}
has (tag) {
return new Promise(resolve => {
const res = this.table_count.get(tag)
return resolve(res && res.cnt > 0)
})
}
}
class TimeChainDataSqliteTagLink extends Interface(InterfaceTagLink) {
constructor () {
super()
constructor(){
super();
this.table = `CREATE TABLE IF NOT EXISTS "taglink" (
"uuid" TEXT NOT NULL,
"tag" TEXT NOT NULL,
"created_at" INTEGER NOT NULL
);`;
this.table_idx = `
this.table = `CREATE TABLE IF NOT EXISTS "taglink" (
"uuid" TEXT NOT NULL,
"tag" TEXT NOT NULL,
"created_at" INTEGER NOT NULL
);`
this.table_idx = `
CREATE INDEX IF NOT EXISTS "uuid_idx" ON "taglink" (
"uuid"
);
CREATE INDEX IF NOT EXISTS "tag_idx" ON "taglink" (
"tag"
);
`;
`
db.exec(this.table + "\n" + this.table_idx);
this.db = db;
}
get table_add(){
if(!this._table_add){
this._table_add = db.prepare('INSERT INTO taglink (uuid,tag,created_at) VALUES (?,?,?)');
}
return this._table_add;
}
db.exec(this.table + '\n' + this.table_idx)
this.db = db
}
get table_delete(){
if(!this._table_delete){
this._table_delete = db.prepare('DELETE FROM taglink WHERE uuid=? AND tag=?');
}
return this._table_delete;
get table_add () {
if (!this._table_add) {
this._table_add = db.prepare('INSERT INTO taglink (uuid,tag,created_at) VALUES (?,?,?)')
}
return this._table_add
}
get table_delete_tag(){
if(!this._table_delete_tag){
this._table_delete_tag = db.prepare('DELETE FROM taglink WHERE tag=?');
}
return this._table_delete_tag;
get table_delete () {
if (!this._table_delete) {
this._table_delete = db.prepare('DELETE FROM taglink WHERE uuid=? AND tag=?')
}
return this._table_delete
}
get table_delete_record(){
if(!this._table_delete_record){
this._table_delete_record = db.prepare('DELETE FROM taglink WHERE uuid=?');
}
return this._table_delete_record;
get table_delete_tag () {
if (!this._table_delete_tag) {
this._table_delete_tag = db.prepare('DELETE FROM taglink WHERE tag=?')
}
return this._table_delete_tag
}
get table_get_records(){
if(!this._table_get_records){
this._table_get_records = db.prepare("SELECT * FROM taglink WHERE tag=?");
}
return this._table_get_records;
get table_delete_record () {
if (!this._table_delete_record) {
this._table_delete_record = db.prepare('DELETE FROM taglink WHERE uuid=?')
}
return this._table_delete_record
}
get table_get_tags(){
if(!this._table_get_tags){
this._table_get_tags = db.prepare("SELECT * FROM taglink WHERE uuid=?");
}
return this._table_get_tags;
get table_get_records () {
if (!this._table_get_records) {
this._table_get_records = db.prepare('SELECT * FROM taglink WHERE tag=?')
}
return this._table_get_records
}
add(uuid,tag){
return new Promise(resolve=>{
const prepare = this.table_add;
const dt = Math.floor(Date.now());
const res = prepare.run(uuid,tag,dt);
return resolve(res?.changes);
});
get table_get_tags () {
if (!this._table_get_tags) {
this._table_get_tags = db.prepare('SELECT * FROM taglink WHERE uuid=?')
}
return this._table_get_tags
}
delete(uuid,tag){
return new Promise(resolve=>{
const prepare = this.table_delete;
const res = prepare.run(uuid,tag);
return resolve(res?.changes);
});
}
add (uuid, tag) {
return new Promise(resolve => {
const prepare = this.table_add
const dt = Math.floor(Date.now())
const res = prepare.run(uuid, tag, dt)
return resolve(res?.changes)
})
}
deleteTag(tag){
return new Promise(resolve=>{
const prepare = this.table_delete_tag;
const res = prepare.run(tag);
return resolve(res?.changes);
});
}
delete (uuid, tag) {
return new Promise(resolve => {
const prepare = this.table_delete
const res = prepare.run(uuid, tag)
return resolve(res?.changes)
})
}
deleteRecord(uuid){
return new Promise(resolve=>{
const prepare = this.table_delete_record;
const res = prepare.run(uuid);
return resolve(res?.changes);
});
}
deleteTag (tag) {
return new Promise(resolve => {
const prepare = this.table_delete_tag
const res = prepare.run(tag)
return resolve(res?.changes)
})
}
getRecords(tag){
return new Promise(resolve=>{
const prepare = this.table_get_records;
const res = prepare.all(tag);
return resolve(res);
})
}
deleteRecord (uuid) {
return new Promise(resolve => {
const prepare = this.table_delete_record
const res = prepare.run(uuid)
return resolve(res?.changes)
})
}
getTags(uuid){
return new Promise(resolve=>{
const prepare = this.table_get_tags;
const res = prepare.all(uuid);
return resolve(res);
})
}
getRecords (tag) {
return new Promise(resolve => {
const prepare = this.table_get_records
const res = prepare.all(tag)
return resolve(res)
})
}
getTags (uuid) {
return new Promise(resolve => {
const prepare = this.table_get_tags
const res = prepare.all(uuid)
return resolve(res)
})
}
}
class TimeChainDataSqliteFile extends Interface(InterfaceFile) {
constructor(){
super();
const table = `CREATE TABLE IF NOT EXISTS "files" (
"uuid" TEXT UNIQUE,
"uuid_record" TEXT NOT NULL,
"timestamp" INTEGER NOT NULL,
"content" BLOB NOT NULL,
"mime" TEXT NOT NULL,
"hash" TEXT,
"created_at" INTEGER NOT NULL,
"updated_at" INTEGER NOT NULL,
constructor () {
super()
const table = `CREATE TABLE IF NOT EXISTS "files" (
"uuid" TEXT UNIQUE,
"uuid_record" TEXT NOT NULL,
"timestamp" INTEGER NOT NULL,
"content" BLOB NOT NULL,
"mime" TEXT NOT NULL,
"hash" TEXT,
"created_at" INTEGER NOT NULL,
"updated_at" INTEGER NOT NULL,
PRIMARY KEY("uuid")
);`;
const table_idx = `
);`
const table_idx = `
CREATE INDEX IF NOT EXISTS "files_idx_record" ON "files" (
"uuid_record"
);
CREATE INDEX IF NOT EXISTS "files_idx_time" ON "files" (
"timestamp" DESC
);`;
"timestamp" DESC
);`
if(!db){
throw new Error("Database is not connected");
}
db.exec(table + "\n" + table_idx);
this.db = db;
if (!db) {
throw new Error('Database is not connected')
}
get table_insert() {
if(!this._table_insert){
this._table_insert = db.prepare(`INSERT INTO files (uuid,uuid_record,timestamp,content,mime,hash,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?)`);
}
return this._table_insert;
db.exec(table + '\n' + table_idx)
this.db = db
}
get table_insert () {
if (!this._table_insert) {
this._table_insert = db.prepare('INSERT INTO files (uuid,uuid_record,timestamp,content,mime,hash,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?)')
}
return this._table_insert
}
get table_update () {
if (!this._table_update) {
this._table_update = db.prepare('UPDATE files SET timestamp = ?, content = ?, mime = ? hash = ?, updated_at = ? WHERE uuid = ?')
}
return this._table_update
}
get table_delete () {
if (!this._table_delete) {
this._table_delete = db.prepare('DELETE FROM files WHERE uuid = ?')
}
return this._table_delete
}
get table_delete_record () {
if (!this._table_delete_record) {
this._table_delete_record = db.prepare('DELETE FROM files WHERE uuid_record = ?')
}
get table_update() {
if(!this._table_update){
this._table_update = db.prepare(`UPDATE files SET timestamp = ?, content = ?, mime = ? hash = ?, updated_at = ? WHERE uuid = ?`);
}
return this._table_update;
}
return this._table_delete_record
}
get table_delete() {
if(!this._table_delete){
this._table_delete = db.prepare(`DELETE FROM files WHERE uuid = ?`);
}
return this._table_delete;
get table_find_one () {
if (!this._table_find_one) {
this._table_find_one = db.prepare('SELECT * FROM files WHERE uuid = ?')
}
return this._table_find_one
}
get table_delete_record() {
if(!this._table_delete_record){
this._table_delete_record = db.prepare(`DELETE FROM files WHERE uuid_record = ?`);
}
return this._table_delete_record;
get table_find_record () {
if (!this._table_find_record) {
this._table_find_record = db.prepare('SELECT * FROM files WHERE uuid_record = ?')
}
return this._table_find_record
}
get table_find_one() {
if(!this._table_find_one){
this._table_find_one = db.prepare(`SELECT * FROM files WHERE uuid = ?`);
}
return this._table_find_one;
}
add (uuid_record, uuid, timestamp, content, mime, hash) {
return new Promise(resolve => {
const dt = Math.floor(Date.now())
const res = this.table_insert.run(uuid, uuid_record, timestamp, content, mime, hash, dt, dt)
return resolve(res?.changes)
})
}
get table_find_record(){
if(!this._table_find_record){
this._table_find_record = db.prepare(`SELECT * FROM files WHERE uuid_record = ?`);
}
return this._table_find_record;
}
getByRecord (uuid_record) {
return new Promise(resolve => {
const res = this.table_find_record.get(uuid_record)
return resolve(res)
})
}
add(uuid_record,uuid,timestamp,content,mime,hash){
return new Promise(resolve=>{
const dt = Math.floor(Date.now());
const res = this.table_insert.run(uuid,uuid_record,timestamp,content,mime,hash,dt,dt);
return resolve(res?.changes);
});
}
get (uuid) {
return new Promise(resolve => {
const res = this.table_find_one.get(uuid)
return resolve(res)
})
}
getByRecord(uuid_record){
return new Promise(resolve => {
const res = this.table_find_record.get(uuid_record);
return resolve(res);
});
}
delete (uuid) {
return new Promise(resolve => {
const prepare = this.table_delete
const res = prepare.run(uuid)
return resolve(res?.changes)
})
}
get(uuid){
return new Promise(resolve => {
const res = this.table_find_one.get(uuid);
return resolve(res);
});
}
deleteRecord (uuid_record) {
return new Promise(resolve => {
const res = this.table_delete_record.run(uuid_record)
return resolve(res?.changes)
})
}
delete(uuid){
return new Promise(resolve=>{
const prepare = this.table_delete;
const res = prepare.run(uuid);
return resolve(res?.changes);
})
}
deleteRecord(uuid_record){
return new Promise(resolve=>{
const res = this.table_delete_record.run(uuid_record);
return resolve(res?.changes);
})
}
update(uuid,timestamp,content,mime,hash){
return new Promise(resolve=>{
const res = this.table_update.run(timestamp,content,mime,hash,uuid);
return resolve(res?.changes);
})
}
update (uuid, timestamp, content, mime, hash) {
return new Promise(resolve => {
const res = this.table_update.run(timestamp, content, mime, hash, uuid)
return resolve(res?.changes)
})
}
}
class TimeChainDataSqliteRecord extends Interface(InterfaceRecord) {
constructor(){
super();
//TODO: Create Tables Here if not exist
const table = `CREATE TABLE IF NOT EXISTS "records" (
"uuid" TEXT NOT NULL UNIQUE,
"timestamp" INTEGER NOT NULL,
"content" TEXT NOT NULL,
"mime" TEXT NOT NULL,
"hash" TEXT,
"created_at" INTEGER NOT NULL,
"updated_at" INTEGER NOT NULL,
constructor () {
super()
// TODO: Create Tables Here if not exist
const table = `CREATE TABLE IF NOT EXISTS "records" (
"uuid" TEXT NOT NULL UNIQUE,
"timestamp" INTEGER NOT NULL,
"content" TEXT NOT NULL,
"mime" TEXT NOT NULL,
"hash" TEXT,
"created_at" INTEGER NOT NULL,
"updated_at" INTEGER NOT NULL,
PRIMARY KEY("uuid")
);`;
const table_idx = `CREATE INDEX IF NOT EXISTS "records_idx_timestamp" ON "records" (
"timestamp" DESC
);`
const table_idx = `CREATE INDEX IF NOT EXISTS "records_idx_timestamp" ON "records" (
"timestamp" DESC
);`
if(!db){
throw new Error("Database is not connected");
}
db.exec(table + "\n" + table_idx);
this.db = db;
if (!db) {
throw new Error('Database is not connected')
}
get table_insert() {
if(!this._table_insert){
this._table_insert = db.prepare(`INSERT INTO records (uuid,timestamp,content,mime,hash,created_at,updated_at) VALUES (?,?,?,?,?,?,?)`);
}
return this._table_insert;
}
db.exec(table + '\n' + table_idx)
this.db = db
}
get table_update(){
if(!this._table_update){
this._table_update = db.prepare(`UPDATE records SET timestamp = ?, content = ?, mime = ?, hash = ? updated_at = ? WHERE uuid = ?`);
}
return this._table_update;
get table_insert () {
if (!this._table_insert) {
this._table_insert = db.prepare('INSERT INTO records (uuid,timestamp,content,mime,hash,created_at,updated_at) VALUES (?,?,?,?,?,?,?)')
}
return this._table_insert
}
get table_delete(){
if(!this._table_delete){
this._table_delete = db.prepare(`DELETE FROM records WHERE uuid = ?`);
}
return this._table_delete;
get table_update () {
if (!this._table_update) {
this._table_update = db.prepare('UPDATE records SET timestamp = ?, content = ?, mime = ?, hash = ? updated_at = ? WHERE uuid = ?')
}
return this._table_update
}
get table_fine_one(){
if(!this._table_fine_one){
this._table_fine_one = db.prepare(`SELECT * FROM records WHERE uuid = ?`);
}
return this._table_fine_one;
get table_delete () {
if (!this._table_delete) {
this._table_delete = db.prepare('DELETE FROM records WHERE uuid = ?')
}
return this._table_delete
}
get table_find(){
if(!this._table_find){
this._table_find = db.prepare(`
select records.*, GROUP_CONCAT(taglink.tag,',') AS tags
get table_fine_one () {
if (!this._table_fine_one) {
this._table_fine_one = db.prepare('SELECT * FROM records WHERE uuid = ?')
}
return this._table_fine_one
}
get table_find () {
if (!this._table_find) {
this._table_find = db.prepare(`
select records.*,
(SELECT GROUP_CONCAT(tag,',') FROM taglink WHERE taglink.uuid = records.uuid) as tags
from records
left join taglink on (taglink.uuid = records.uuid)
group by records.uuid
order by records.timestamp DESC
limit ? offset ?
`);
`)
}
return this._table_find
}
generator_find_sql (tags) {
let query = `
select records.*,
(SELECT GROUP_CONCAT(tag,',') FROM taglink WHERE taglink.uuid = records.uuid) as tags
from records
left join taglink on (taglink.uuid = records.uuid)
{where}
group by records.uuid
order by records.timestamp DESC
limit ? offset ?
`;
const where = [];
tags.forEach(tag => {
if( tag.indexOf(':') != -1 ){
//Skip Filter
const parts = tag.split(':');
const cmd = parts[0];
const dayjs = require('dayjs');
switch (cmd){
case 'before':
const time_before = Math.floor(dayjs(parts[1]).toDate().getTime());
where.push(`record.timestamp < ${time_before}`);
break;
case 'after':
const time_after = Math.floor(dayjs(parts[1]).toDate().getTime());
where.push(`record.timestamp > ${time_after}`);
break;
}
}else{
let cleantag = tag.replace(/\'/g,"''");
where.push(`taglink.tag = '${cleantag}'`);
}
return this._table_find;
}
});
add(uuid,timestamp,content,mime,hash){
return new Promise(resolve=>{
const dt = Math.floor(Date.now());
const res = this.table_insert.run(uuid,timestamp,content,mime,hash,dt,dt);
return resolve(res?.changes);
});
}
get(uuid){
return new Promise(resolve=>{
const rec = this.table_fine_one.get(uuid);
return resolve(rec);
});
}
let where_string = "WHERE " + where.join(" AND ");
find(search,sort=null,limit=undefined,offset=0){
//TODO: add logic for seach and sort (Currently does nothing)
return new Promise(resolve=>{
const prepare = this.table_find;
query = query.replace('{where}', where_string);
const res = prepare.all(100,0);
console.log(query);
//TODO: add count here
const rescount = {cnt:100};
return db.prepare(query);
return resolve({
data: res,
count: rescount?.cnt,
limit: limit,
offset: offset
});
});
}
}
delete(uuid){
return new Promise(resolve=>{
const prepare = this.table_delete;
const res = prepare.run(uuid);
return resolve(res?.changes);
});
}
add (uuid, timestamp, content, mime, hash) {
return new Promise(resolve => {
const dt = Math.floor(Date.now())
const res = this.table_insert.run(uuid, timestamp, content, mime, hash, dt, dt)
return resolve(res?.changes)
})
}
update(uuid,content,mime,hash){
return new Promise(resolve=>{
const dt = Math.floor(Date.now());
const res = this.table_update.run(content,mime,hash,dt,uuid);
return resolve(res?.changes);
get (uuid) {
return new Promise(resolve => {
const rec = this.table_fine_one.get(uuid)
return resolve(rec)
})
}
find (search, sort = null, limit = undefined, offset = 0) {
// TODO: add logic for seach and sort (Currently does nothing)
return new Promise(resolve => {
if(!search || !search.tags || Object.keys(search.tags).length === 0){
const prepare = this.table_find
const res = prepare.all(100, 0)
// TODO: add count here
const rescount = { cnt: 100 }
return resolve({
data: res,
count: rescount?.cnt,
limit: limit,
offset: offset
})
}
}else{
console.log(search);
const prepare = this.generator_find_sql(search.tags);
const res = prepare.all(100, 0)
const rescount = { cnt: 100 }
return resolve({
data: res,
count: rescount?.cnt,
limit: limit,
offset: offset
})
}
})
}
delete (uuid) {
return new Promise(resolve => {
const prepare = this.table_delete
const res = prepare.run(uuid)
return resolve(res?.changes)
})
}
update (uuid, content, mime, hash) {
return new Promise(resolve => {
const dt = Math.floor(Date.now())
const res = this.table_update.run(content, mime, hash, dt, uuid)
return resolve(res?.changes)
})
}
}
class TimeChainDataSqlite {
}
module.exports = {
ConnectToDatabase,
TimeChainDataSqlite,
TimeChainDataSqliteRecord,
TimeChainDataSqliteFile,
TimeChainDataSqliteTagLink,
TimeChainDataSqliteTag
ConnectToDatabase,
TimeChainDataSqlite,
TimeChainDataSqliteRecord,
TimeChainDataSqliteFile,
TimeChainDataSqliteTagLink,
TimeChainDataSqliteTag
}

View File

@ -13,6 +13,12 @@
import TimechainInput from './timechain-input.riot'
import TimechainList from './timechain-list.riot'
import AppDivider from './app-divider.riot'
import keymage from 'keymage';
import pubsub from 'pubsub-js';
if(window.Timechain_isElectron){
const { ipcRenderer } = window.require('electron')
}
export default {
components: {
@ -20,7 +26,103 @@
TimechainInput,
TimechainList,
AppDivider
},
onBeforeMount(){
console.log('Init');
},
onMounted(){
console.log("Application Mounted");
if(window.Timechain_isElectron){
console.log("Is Electron");
this.setupElectronKeyEvents();
}else{
console.log("Is Browser");
}
},
setupElectronKeyEvents(){
const kevents = [];
kevents.push( keymage('ctrl-o', this.onElectronOpenDatabase.bind(this)) );
kevents.push( keymage('ctrl-n', this.onElectronCreateDatabase.bind(this)) );
this._keyEvents = kevents;
},
async onElectronOpenDatabase(){
const file = await ipcRenderer.invoke('select-sqlite-file',{});
console.log(file)
const filepath = (file && file.filePaths) ? file.filePaths[0] : false;
console.log(filepath);
if(!filepath){
return alert("Could not load file. Filename not provided.")
}
console.log("Loading new database");
const loaded = await ipcRenderer.invoke('timechain-database-open',{filename:filepath});
if(!loaded){
return alert("Database loading failed");
}else{
console.log("%c Database Reloaded","background-color:red;color:white;");
pubsub.publish('database-reloaded',true);
}
},
async onElectronCreateDatabase(){
console.log("Create Datbase Event");
const file = await ipcRenderer.invoke('create-sqlite-file',{});
console.log("Returned")
console.log(file);
//const filepath = (file && file.filePaths) ? file.filePaths[0] : false;
//console.log(filepath);
const filepath = file;
if(!filepath){
console.warn("No file name");
return alert("Could not create file. Filename not provided.",filepath);
}else{
console.log("We have a file name.. Time to make db");
}
console.log("Creating new database")
const loaded = await ipcRenderer.invoke('timechain-database-create',{filename:filepath});
if(!loaded){
return alert("Database not created");
}else{
console.log("%c Database Created","background-color:red;color:white;");
pubsub.publish('database-reloaded', true);
}
}
/*onElectronCreateDatabaseOld(){
return ipcRenderer.invoke('select-sqlite-file',{}).then(file=>{
console.log(file)
const filepath = (file && file.filePaths) ? file.filePaths[0] : false;
console.log(filepath);
if(!filepath){
return alert("Could not load file. Filename not provided.")
return ipcRenderer.invoke('timechain-database-open',{}).then(success=>{
console.log("DB RETURNED");
if(success){
console.log("%c Database Reloaded","background-color:red;color:white;");
pubsub.publish('database-reloaded',true);
}else{
console.log("Database loading failed");
}
return success;
})
}
});
}*/
}
</script>

View File

@ -54,6 +54,10 @@
onMounted(){
document.addEventListener('paste', this.pasteEvent.bind(this));
this.event_dbreloaded = pubsub.subscribe('database-reloaded', (event,res)=>{
pubsub.publish('timechain-list-update', true);
})
this.setupKeyEvents();
},
/**
@ -86,6 +90,7 @@
this._keyevents.forEach(f=>{
f();
});
pubsub.unsubscribe(this.event_dbreloaded);
},
/**
* Window paste event has been fire.

View File

@ -1,5 +1,11 @@
<timechain-list>
<div class="search-row">
<div class="search-tag">
<timechain-tag ontag="{onTags}" />
</div>
</div>
<div class="top-gradient"></div>
<div class="timechain-list">
@ -41,6 +47,9 @@
import TimechainTagList from './timechain-tag-list.riot'
import pubsub from 'pubsub-js'
import TimechainTag from './timechain-tag.riot'
const {
TimeChainDataSqliteRecord
} = require('../data/sqlite-client');
@ -54,7 +63,8 @@
components: {
Raw,
TimestampStatic,
TimechainTagList
TimechainTagList,
TimechainTag
},
onMounted(){
console.log("List mounted");
@ -67,11 +77,19 @@
pubsub.unsubscribe(this.event_load);
},
loadRecords(){
loadRecords(search){
this.update({loading:true});
const TR = new TimeChainDataSqliteRecord();
TR.find({},null,100,0).then(records=>{
if(!search) {
if(this.last_search){
search = this.last_search
}else{
search = {};
}
}
TR.find(search,null,100,0).then(records=>{
console.log("Records", records);
this.update({records:records.data,loading:false});
}).then(()=>{
@ -88,6 +106,25 @@
});
this.update();
},
onTags(tags){
if(tags==''){
return this.loadRecords({});
}
console.log(tags);
tags = JSON.parse(tags);
const search = [];
tags.map(t=>{
//console.log(t.value);
search.push(t.value);
});
this.loadRecords({
tags:search
})
}
}

View File

@ -1,6 +1,6 @@
<timechain-tag>
<div class="timechain-tagging">
<input id="tagging" placeholder="tag your paste">
<input id="{state.id}" placeholder="tag your paste">
</div>
<script>
@ -8,8 +8,14 @@
const pubsub = require('pubsub-js');
export default {
state:{
id:'tagging'
},
onBeforeMount(){
this.state.id = "tagging"+this.makeid(5);
},
onMounted(){
const inputElm = this.$('#tagging');
const inputElm = this.$('#'+this.state.id);
this.tagify = new Tagify(inputElm);
inputElm.addEventListener('change', this.onChange.bind(this));
this.event_clean = pubsub.subscribe('tag.clean', this.onClean.bind(this));
@ -22,6 +28,15 @@
},
onClean(){
this.tagify.removeAllTags();
},
makeid(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
}
</script>

View File

@ -1,34 +1,14 @@
import './src/less/main.less';
import './src/css/tagify.scss';
import * as Sentry from "@sentry/browser";
import { BrowserTracing } from "@sentry/tracing";
Sentry.init({
dsn: "http://a12d5c5f800b406f8d1c0c5d2ed63a78@216.128.138.128:8000/1",
// Alternatively, use `process.env.npm_package_version` for a dynamic release version
// if your build tool supports it.
release: "timechain@1.0.0",
integrations: [new BrowserTracing()],
// Set tracesSampleRate to 1.0 to capture 100%
// of transactions for performance monitoring.
// We recommend adjusting this value in production
tracesSampleRate: 1.0,
});
import './src/less/main.less'
import './src/css/tagify.scss'
import App from './src/ui/app.riot'
import { component } from 'riot'
window.debugging = true;
setTimeout(()=>{
document.getElementById('main-loading')?.remove();
component(App)(document.getElementById('timechain'));
Sentry.captureMessage('Application Started');
},0)
window.debugging = true
setTimeout(() => {
document.getElementById('main-loading')?.remove()
component(App)(document.getElementById('timechain'))
Sentry.captureMessage('Application Started')
}, 0)