Compare commits

..

No commits in common. "7c9a29b16c99cb21cc27c60a4e6321f8f814d5cb" and "54f16d0ac262e588410f6c50b667e6eea626442f" have entirely different histories.

17 changed files with 515 additions and 5763 deletions

80
.gitignore vendored
View File

@ -1,40 +1,40 @@
# Logs # Logs
logs logs
*.log *.log
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
lerna-debug.log* lerna-debug.log*
.pnpm-debug.log* .pnpm-debug.log*
.npm .npm
# yarn v2 # yarn v2
.yarn/cache .yarn/cache
.yarn/unplugged .yarn/unplugged
.yarn/build-state.yml .yarn/build-state.yml
.yarn/install-state.gz .yarn/install-state.gz
.pnp.* .pnp.*
# Dependency directories # Dependency directories
node_modules/ node_modules/
jspm_packages/ jspm_packages/
# dotenv environment variable files # dotenv environment variable files
.env .env
.env.development.local .env.development.local
.env.test.local .env.test.local
.env.production.local .env.production.local
.env.local .env.local
# IDEs # IDEs
.vscode .vscode
.idea .idea
# parcel-bundler cache (https://parceljs.org/) # parcel-bundler cache (https://parceljs.org/)
.cache .cache
.parcel-cache .parcel-cache
# build directory # build directory
dist/ dist/

View File

@ -1,6 +1,6 @@
{ {
"extends": "@parcel/config-default", "extends": "@parcel/config-default",
"transformers": { "transformers": {
"*.riot": ["@riotjs/parcel-transformer-riot"] "*.riot": ["@riotjs/parcel-transformer-riot"]
} }
} }

View File

@ -1,17 +0,0 @@
# TimeChain
A straightforward program for pasting text, HTML, photos, or anything else. The FILO interface is straightforward: tag it, timestamp it, then view it.
# Build
```
npm run build
```
# Tests
Type the following commands to run tests:
```
npx jest
```

View File

@ -1,21 +1,21 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'"> <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'"> <meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<title>Time Chain</title> <title>Time Chain</title>
<link rel="stylesheet" href="src/css/main.css"> <link rel="stylesheet" href="src/css/main.css">
</head> </head>
<body> <body>
<div class="loading" > <div class="loading" >
<h1>Time Chain!</h1> <h1>Time Chain!</h1>
<p>Time traveling your data! Comming soon...</p> <p>Time traveling your data! Comming soon...</p>
<p id="timestamp"></p> <p id="timestamp"></p>
<img src="src/img/chain2.svg" class="rotate"> <img src="src/img/chain2.svg" class="rotate">
</div> </div>
<script type="module" src="src/dist/start.js"></script> <script type="module" src="src/dist/start.js"></script>
</body> </body>
</html> </html>

5035
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@
}, },
"scripts": { "scripts": {
"start": "electron .", "start": "electron .",
"build": "parcel build --dist-dir ./src/dist --target \"jason\" start.js" "compile": "parcel build --dist-dir ./src/dist --target \"jason\" start.js"
}, },
"browserslist": "> 0.5%, last 2 versions, not dead", "browserslist": "> 0.5%, last 2 versions, not dead",
"targets": { "targets": {
@ -25,14 +25,12 @@
"@riotjs/compiler": "^6.1.3", "@riotjs/compiler": "^6.1.3",
"@riotjs/parcel-transformer-riot": "^7.0.3", "@riotjs/parcel-transformer-riot": "^7.0.3",
"electron": "^16.0.7", "electron": "^16.0.7",
"jest": "^27.4.7",
"parcel": "^2.2.1" "parcel": "^2.2.1"
}, },
"dependencies": { "dependencies": {
"better-sqlite3": "^7.5.0", "better-sqlite3": "^7.5.0",
"dayjs": "^1.10.7", "dayjs": "^1.10.7",
"es6-interface": "^3.2.1", "es6-interface": "^3.2.1",
"nanoid": "^3.2.0",
"pubsub-js": "^1.9.4", "pubsub-js": "^1.9.4",
"riot": "^6.1.2" "riot": "^6.1.2"
} }

View File

@ -1,31 +1,31 @@
body { body {
background-color: #333; background-color: #333;
color: whitesmoke; color: whitesmoke;
} }
.loading { .loading {
text-align: center; text-align: center;
padding: 5em; padding: 5em;
} }
.loading img { .loading img {
width: 30%; width: 30%;
} }
.loading p { .loading p {
font-weight: 700; font-weight: 700;
font-size: 1.2em; font-size: 1.2em;
} }
.rotate { .rotate {
animation: rotation 2s infinite linear; animation: rotation 2s infinite linear;
} }
@keyframes rotation { @keyframes rotation {
from { from {
transform: rotate(0deg); transform: rotate(0deg);
} }
to { to {
transform: rotate(359deg); transform: rotate(359deg);
} }
} }

View File

@ -1,35 +1,35 @@
const InterfaceRecord = { const InterfaceRecord = {
find(search,sort,limit,offset){}, find(search,limit,offset){},
get(uuid){}, get(uuid){},
add(uuid,timestamp,content,mime,hash){}, add(uuid,timestamp,content,mime,hash){},
update(uuid,content,mime,hash){}, update(uuid,timestamp,content,mime,hash){},
delete(uuid){}, delete(uuid){},
} }
const InterfaceFile = { const InterfaceFile = {
add(uuid_record,uuid,timestamp,content,mime,hash){}, add(uuid_record,uuid,timestamp,content,mime,hash){},
getByRecord(uuid_record){}, getByRecord(uuid_record){},
get(uuid){}, get(uuid){},
delete(uuid){}, delete(uuid){},
deleteRecord(uuid_record){}, deleteRecord(uuid_record){},
update(uuid,timestamp,content,mime,hash){} update(uuid,timestamp,content,mime,hash){}
} }
const InterfaceTag = { const InterfaceTag = {
add(tag){}, add(uuid,word){},
delete(tag){} delete(uuid){}
} }
const InterfaceTagLink = { const InterfaceTagLink = {
add(uuid,tag){}, add(uuid_record,uuid_tag){},
delete(uuid,tag){}, delete(uuid_record,uuid_tag){},
deleteTag(tag){}, deleteTag(uuid){},
deleteRecord(uuid){} deleteRecord(uuid){}
} }
module.exports = { module.exports = {
InterfaceTagLink, InterfaceTagLink,
InterfaceTag, InterfaceTag,
InterfaceFile, InterfaceFile,
InterfaceRecord InterfaceRecord
} }

View File

@ -1,76 +1,76 @@
const { ipcMain } = require('electron'); const { ipcMain } = require('electron');
const {app} = require('electron'); const {app} = require('electron');
const config = app.getPath('userData'); const config = app.getPath('userData');
const Conf = require('conf'); const Conf = require('conf');
const config = new Conf(); const config = new Conf();
// ** Extra Data // ** Extra Data
ipcMain.on('timechain-config-dir', (event,arg) => { ipcMain.on('timechain-config-dir', (event,arg) => {
const configDir = app.getPath('userData'); const configDir = app.getPath('userData');
event.reply('timechain-config-dir-reply', configDir); event.reply('timechain-config-dir-reply', configDir);
}); });
// ** RECORD ** // ** RECORD **
ipcMain.on('timechain-record-add', (event, arg) => { ipcMain.on('timechain-record-add', (event, arg) => {
event.reply('timechain-record-add-reply', 'pong') event.reply('timechain-record-add-reply', 'pong')
}); });
ipcMain.on('timechain-record-delete', (event, arg) => { ipcMain.on('timechain-record-delete', (event, arg) => {
event.reply('timechain-record-delete-reply', 'pong') event.reply('timechain-record-delete-reply', 'pong')
}); });
ipcMain.on('timechain-record-update', (event, arg) => { ipcMain.on('timechain-record-update', (event, arg) => {
event.reply('timechain-record-update-reply', 'pong') event.reply('timechain-record-update-reply', 'pong')
}); });
ipcMain.on('timechain-record-find', (event, arg) => { ipcMain.on('timechain-record-find', (event, arg) => {
event.reply('timechain-record-find-reply', 'pong') event.reply('timechain-record-find-reply', 'pong')
}); });
// ** FILE ** // ** FILE **
ipcMain.on('timechain-file-find', (event, arg) => { ipcMain.on('timechain-file-find', (event, arg) => {
event.reply('timechain-file-find-reply', 'pong') event.reply('timechain-file-find-reply', 'pong')
}); });
ipcMain.on('timechain-file-add', (event, arg) => { ipcMain.on('timechain-file-add', (event, arg) => {
event.reply('timechain-file-add-reply', 'pong') event.reply('timechain-file-add-reply', 'pong')
}); });
ipcMain.on('timechain-file-update', (event, arg) => { ipcMain.on('timechain-file-update', (event, arg) => {
event.reply('timechain-file-update-reply', 'pong') event.reply('timechain-file-update-reply', 'pong')
}); });
ipcMain.on('timechain-file-delete', (event, arg) => { ipcMain.on('timechain-file-delete', (event, arg) => {
event.reply('timechain-file-delete-reply', 'pong') event.reply('timechain-file-delete-reply', 'pong')
}); });
// ** TAG ** // ** TAG **
ipcMain.on('timechain-tag-add', (event, arg) => { ipcMain.on('timechain-tag-add', (event, arg) => {
event.reply('timechain-tag-add-reply', 'pong') event.reply('timechain-tag-add-reply', 'pong')
}); });
ipcMain.on('timechain-tag-delete', (event, arg) => { ipcMain.on('timechain-tag-delete', (event, arg) => {
event.reply('timechain-tag-delete-reply', 'pong') event.reply('timechain-tag-delete-reply', 'pong')
}); });
// ** TAG LINK ** // ** TAG LINK **
ipcMain.on('timechain-taglink-add', (event, arg) => { ipcMain.on('timechain-taglink-add', (event, arg) => {
event.reply('timechain-taglink-add-reply', 'pong') event.reply('timechain-taglink-add-reply', 'pong')
}); });
ipcMain.on('timechain-taglink-delete', (event, arg) => { ipcMain.on('timechain-taglink-delete', (event, arg) => {
event.reply('timechain-taglink-delete-reply', 'pong') event.reply('timechain-taglink-delete-reply', 'pong')
}); });
ipcMain.on('timechain-taglink-deleteTag', (event, arg) => { ipcMain.on('timechain-taglink-deleteTag', (event, arg) => {
event.reply('timechain-taglink-deleteTag-reply', 'pong') event.reply('timechain-taglink-deleteTag-reply', 'pong')
}); });
ipcMain.on('timechain-taglink-deleteRecord', (event, arg) => { ipcMain.on('timechain-taglink-deleteRecord', (event, arg) => {
event.reply('timechain-taglink-deleteRecord-reply', 'pong') event.reply('timechain-taglink-deleteRecord-reply', 'pong')
}); });

View File

@ -1,392 +1,264 @@
const Interface = require("es6-interface"); const Interface = require("es6-interface");
const {InterfaceRecord,InterfaceFile,InterfaceTag,InterfaceTagLink} = require("./interfaces"); const {InterfaceRecord,InterfaceFile,InterfaceTag,InterfaceTagLink} = require("./interfaces");
let db = null; let db = null;
const ConnectToDatabase = (path)=>{ const connectToDatabase = (path)=>{
db = require('better-sqlite3')(path, {}); db = require('better-sqlite3')(path, {});
return db; }
}
class TimeChainDataSqliteTag extends Interface(InterfaceTag) {
class TimeChainDataSqliteTag extends Interface(InterfaceTag) {
constructor(){
constructor(){
super(); }
const table = `CREATE TABLE IF NOT EXISTS "tags" (
"tag" TEXT UNIQUE, add(uuid,word){
"created_at" INTEGER NOT NULL,
"updated_at" INTEGER NOT NULL, }
PRIMARY KEY("tag")
);`; delete(uuid){
const table_idx = ``; }
if(!db){ }
throw new Error("Database is not connected");
} class TimeChainDataSqliteTagLink extends Interface(InterfaceTagLink) {
constructor(){
db.exec(table + "\n" + table_idx);
this.db = db; }
}
}
get table_add(){
if(!this._table_add){ class TimeChainDataSqliteFile extends Interface(InterfaceFile) {
this._table_add = db.prepare("INSERT INTO tags (tag,created_at,updated_at) VALUES (?,?,?)");
} constructor(){
return this.table_add; const table = `CREATE TABLE IF NOT EXISTS "files" (
} "uuid" TEXT UNIQUE,
"uuid_record" TEXT NOT NULL,
get table_delete(){ "timestamp" INTEGER NOT NULL,
if(!this._table_delete){ "content" BLOB NOT NULL,
this._table_delete = db.prepare("DELETE FROM tags WHERE tag = ?"); "mime" TEXT NOT NULL,
} "hash" TEXT,
return this._table_delete; "created_at" INTEGER NOT NULL,
} "updated_at" INTEGER NOT NULL,
PRIMARY KEY("uuid")
add(tag){ );`;
return new Promise(resolve=>{ const table_idx = `
const dt = Math.floor(Date.now()); CREATE INDEX IF NOT EXISTS "files_idx_record" ON "files" (
const res = this.table_add.run(tag,dt,dt); "uuid_record"
return resolve(res?.changes); );
}); CREATE INDEX IF NOT EXISTS "files_idx_time" ON "files" (
} "timestamp" DESC
);`;
delete(tag){
return new Promise(resolve=>{ if(!db){
const dt = Math.floor(Date.now()); throw new Error("Database is not connected");
const res = this.table_delete.run(tag); }
return resolve(res?.changes);
}); db.exec(table + "\n" + table_idx);
} this.db = db;
}
}
get table_insert() {
class TimeChainDataSqliteTagLink extends Interface(InterfaceTagLink) { if(!this._table_insert){
this._table_insert = db.prepare(`INSERT INTO files (uuid,uuid_record,timestamp,content,mime,hash,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?)`);
constructor(){ }
super(); return this._table_insert;
}
this.table = `CREATE TABLE IF NOT EXISTS "taglink" (
"uuid" TEXT NOT NULL, get table_update() {
"tag" TEXT NOT NULL, if(!this._table_update){
"created_at" INTEGER NOT NULL this._table_update = db.prepare(`UPDATE files SET timestamp = ?, content = ?, mime = ? hash = ?, updated_at = ? WHERE uuid = ?`);
);`; }
this.table_idx = ` return this._table_update;
CREATE INDEX IF NOT EXISTS "uuid_idx" ON "taglink" ( }
"uuid"
); get table_delete() {
CREATE INDEX IF NOT EXISTS "tag_idx" ON "taglink" ( if(!this._table_delete){
"tag" this._table_delete = db.prepare(`DELETE FROM files WHERE uuid = ?`);
); }
`; return this._table_delete;
}
db.exec(this.table + "\n" + this.table_idx);
this.db = db; get table_delete_record() {
}
if(!this._table_delete_record){
get table_add(){ this._table_delete_record = db.prepare(`DELETE FROM files WHERE uuid_record = ?`);
if(!_table_add){ }
this._table_add = db.prepare('INSERT INTO taglink (uuid,tag,created_at) VALUES (?,?,?)');
} return this._table_delete_record;
return this._table_add; }
}
get table_find_one() {
get table_delete(){ if(!this._table_find_one){
if(!this._table_delete){ this._table_find_one = db.prepare(`SELECT * FROM files WHERE uuid = ?`);
this._table_delete = db.prepare('DELETE FROM taglink WHERE uuid=? AND tag=?'); }
} return this._table_find_one;
return this._table_delete; }
}
get table_find_record(){
get table_delete_tag(){ if(!this._table_find_record){
if(!this._table_delete_tag){ this._table_find_record = db.prepare(`SELECT * FROM files WHERE uuid_record = ?`);
this._table_delete_tag = db.prepare('DELETE FROM taglink WHERE tag=?'); }
} return this._table_find_record;
return this._table_delete_tag; }
}
add(uuid_record,uuid,timestamp,content,mime,hash){
get table_delete_record(){ return new Promise(resolve=>{
if(!this._table_delete_record){ const dt = Math.floor(Date.now());
this._table_delete_record = db.prepare('DELETE FROM taglink WHERE uuid=?'); const res = this.table_insert.exec(uuid,uuid_record,timestamp,content,mime,hash,dt,dt);
} return resolve(res?.changes);
} });
}
add(uuid,tag){
return new Promise(resolve=>{ getByRecord(uuid_record){
const prepare = this.table_add; return new Promise(resolve => {
const dt = Math.floor(Date.now()); const res = this.table_find_record.get(uuid_record);
const res = prepare.run(uuid,tag,dt); return resolve(res);
return resolve(res?.changes); });
}); }
}
get(uuid){
delete(uuid,tag){ return new Promise(resolve => {
return new Promise(resolve=>{ const res = this.table_fine_one.get(uuid);
const prepare = this.table_delete; return resolve(res);
const res = prepare.run(uuid,tag); });
return resolve(res?.changes); }
});
} delete(uuid){
return new Promise(resolve=>{
deleteTag(tag){ const res = this.table_delete.exec(uuid);
return new Promise(resolve=>{ return resolve(res?.changes);
const prepare = this.table_delete_tag; })
const res = prepare.run(tag); }
return resolve(res?.changes);
}); deleteRecord(){
} return new Promise(resolve=>{
const res = this.table_delete_record.exec(uuid_record);
deleteRecord(uuid){ return resolve(res?.changes);
return new Promise(resolve=>{ })
const prepare = this.table_delete_record; }
const res = prepare.run(uuid);
return resolve(res?.changes); update(uuid,timestamp,content,mime,hash){
}); return new Promise(resolve=>{
} const res = this.table_update.exec(timestamp,content,mime,hash,uuid);
} return resolve(res?.changes);
})
class TimeChainDataSqliteFile extends Interface(InterfaceFile) { }
}
constructor(){
super(); class TimeChainDataSqliteRecord extends Interface(InterfaceRecord) {
const table = `CREATE TABLE IF NOT EXISTS "files" (
"uuid" TEXT UNIQUE, constructor(){
"uuid_record" TEXT NOT NULL, //TODO: Create Tables Here if not exist
"timestamp" INTEGER NOT NULL, const table = `CREATE TABLE IF NOT EXISTS "records" (
"content" BLOB NOT NULL, "uuid" TEXT NOT NULL UNIQUE,
"mime" TEXT NOT NULL, "timestamp" INTEGER NOT NULL,
"hash" TEXT, "content" TEXT NOT NULL,
"created_at" INTEGER NOT NULL, "mime" TEXT NOT NULL,
"updated_at" INTEGER NOT NULL, "hash" TEXT,
PRIMARY KEY("uuid") "created_at" INTEGER NOT NULL,
);`; "updated_at" INTEGER NOT NULL,
const table_idx = ` PRIMARY KEY("uuid")
CREATE INDEX IF NOT EXISTS "files_idx_record" ON "files" ( );`;
"uuid_record" const table_idx = `CREATE INDEX IF NOT EXISTS "records_idx_timestamp" ON "records" (
); "timestamp" DESC
CREATE INDEX IF NOT EXISTS "files_idx_time" ON "files" ( );`
"timestamp" DESC
);`; 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;
db.exec(table + "\n" + table_idx); }
this.db = db;
} get table_insert() {
if(!this._table_insert){
get table_insert() { this._table_insert = db.prepare(`INSERT INTO records (uuid,timestamp,content,mime,hash,created_at,updated_at) VALUES (?,?,?,?,?,?,?)`);
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;
} }
return this._table_insert;
} get table_update(){
if(!this._table_update){
get table_update() { this._table_update = db.prepare(`UPDATE records SET timestamp = ?, content = ?, mime = ?, hash = ? updated_at = ? WHERE uuid = ?`);
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_update;
} get table_delete(){
if(!this._table_delete){
get table_delete() { this._table_delete = db.prepare(`DELETE FROM records WHERE uuid = ?`);
if(!this._table_delete){ }
this._table_delete = db.prepare(`DELETE FROM files WHERE uuid = ?`); return this._table_delete;
} }
return this._table_delete;
} get table_fine_one(){
if(!this._table_fine_one){
get table_delete_record() { this._table_fine_one = db.prepare(`SELECT * FROM records WHERE uuid = ?`);
}
if(!this._table_delete_record){ return this._table_fine_one;
this._table_delete_record = db.prepare(`DELETE FROM files WHERE uuid_record = ?`); }
}
add(uuid,timestamp,content,mime,hash){
return this._table_delete_record; return new Promise(resolve=>{
} const dt = Math.floor(Date.now());
const res = this.table_insert.run(uuid,timestamp,content,mime,hash,dt,dt);
get table_find_one() { return resolve(res?.changes);
if(!this._table_find_one){ });
this._table_find_one = db.prepare(`SELECT * FROM files WHERE uuid = ?`); }
}
return this._table_find_one; get(uuid){
} return new Promise(resolve=>{
const rec = this.table_fine_one.get(uuid);
get table_find_record(){ return resolve(rec);
if(!this._table_find_record){ });
this._table_find_record = db.prepare(`SELECT * FROM files WHERE uuid_record = ?`); }
}
return this._table_find_record; find(search,sort=null,limit=undefined,offset=0){
} //TODO: add logic for seach and sort (Currently does nothing)
return new Promise(resolve=>{
add(uuid_record,uuid,timestamp,content,mime,hash){ const rescount = this.db.prepare("SELECT count(*) as cnt FROM records").get();
return new Promise(resolve=>{
const dt = Math.floor(Date.now()); if(limit){
const res = this.table_insert.run(uuid,uuid_record,timestamp,content,mime,hash,dt,dt); const res = this.db.prepare("SELECT * FROM records ORDER BY timestamp DESC LIMIT ? OFFSET ?").all(limit,offset);
return resolve(res?.changes); }else{
}); const res = this.db.prepare("SELECT * FROM records ORDER BY timestamp DESC").all();
} }
getByRecord(uuid_record){ return resolve({
return new Promise(resolve => { data: res,
const res = this.table_find_record.get(uuid_record); count: rescount?.cnt,
return resolve(res); limit: limit,
}); offset: offset
} });
get(uuid){ })
return new Promise(resolve => { }
const res = this.table_fine_one.get(uuid);
return resolve(res); delete(uuid){
}); return new Promise(resolve=>{
} const res = this.table_delete.exec(uuid);
return resolve(res?.changes);
delete(uuid){ });
return new Promise(resolve=>{ }
const prepare = this.table_delete;
const res = prepare.exec(uuid); update(uuid,content,mime,hash){
return resolve(res?.changes); return new Promise(resolve=>{
}) const dt = Math.floor(Date.now());
} const res = this.table_update.exec(content,mime,hash,dt,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); }
})
} class TimeChainDataSqlite {
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,
PRIMARY KEY("uuid")
);`;
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;
}
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_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_delete(){
if(!this._table_delete){
this._table_delete = db.prepare(`DELETE FROM records WHERE uuid = ?`);
}
return this._table_delete;
}
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;
}
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);
});
}
find(search,sort=null,limit=undefined,offset=0){
//TODO: add logic for seach and sort (Currently does nothing)
return new Promise(resolve=>{
const rescount = this.db.prepare("SELECT count(*) as cnt FROM records").get();
if(limit){
const res = this.db.prepare("SELECT * FROM records ORDER BY timestamp DESC LIMIT ? OFFSET ?").all(limit,offset);
}else{
const res = this.db.prepare("SELECT * FROM records ORDER BY timestamp DESC").all();
}
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
}

View File

@ -1,3 +0,0 @@
<app>
</app>

View File

View File

@ -1,7 +1,7 @@
import Timestamp from './src/ui/timestamp.riot' import Timestamp from './timestamp.riot'
import { component } from 'riot' import { component } from 'riot'
component(Timestamp)(document.getElementById('timestamp')) component(Timestamp)(document.getElementById('timestamp'))
console.log("yes!"); console.log("yes!");

View File

@ -1,77 +0,0 @@
const {TimeChainDataSqliteRecord,ConnectToDatabase, TimeChainDataSqliteFile, TimeChainDataSqliteTag, TimeChainDataSqliteTagLink} = require('../src/data/sqlite');
const { nanoid } = require('nanoid');
const { unsubscribe } = require('pubsub-js');
let db = null;
beforeAll(() => {
db = ConnectToDatabase(__dirname + "/test.db");
});
afterAll(()=>{
db.close();
})
test("Should create a records table", ()=>{
const rec = new TimeChainDataSqliteRecord();
const st = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?");
const res = st.get('records');
expect(res).not.toBeNull();
expect(res.name).toEqual('records');
});
test("Should create a files table", ()=>{
const file = new TimeChainDataSqliteFile();
const st = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?");
const res = st.get('files');
expect(res).not.toBeNull();
expect(res.name).toEqual('files');
});
test("Should create a tags table", ()=>{
const tags = new TimeChainDataSqliteTag();
const st = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?");
const res = st.get('tags');
expect(res).not.toBeNull();
expect(res.name).toEqual('tags');
});
test("Should create a tags link table", ()=>{
const links = new TimeChainDataSqliteTagLink();
const st = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?");
const res = st.get('taglink');
expect(res).not.toBeNull();
expect(res.name).toEqual('taglink');
});
test("Should create a record in the database and then remove it",async ()=>{
const rec = new TimeChainDataSqliteRecord();
const id = nanoid();
const hash = "fakehash";
const content = "This is a test";
const mime = "text/plain";
const ts = Math.floor(Date.now());
return rec.add(id,ts,content,mime,hash).then(res=>{
expect(res).toEqual(1);
return rec.get(id).then(res=>{
expect(res.uuid).toEqual(id);
expect(res.timestamp).toEqual(ts);
expect(res.mime).toEqual(mime);
expect(res.content).toEqual(content);
expect(res.hash).toEqual(hash);
return rec.delete(id);
})
})
})

Binary file not shown.

View File

@ -1,34 +1,34 @@
<timestamp> <timestamp>
<div class="timestamp"> <div class="timestamp">
<div class="timestamp-icon"> <div class="timestamp-icon">
<div class="timestamp-text">{ state.time_text }</div> <div class="timestamp-text">{ state.time_text }</div>
</div> </div>
<script> <script>
const pubsub = require('pubsub-js'); const pubsub = require('pubsub-js');
const dayjs = require('dayjs'); const dayjs = require('dayjs');
export default { export default {
state: { state: {
time_text: "", time_text: "",
format: "long" format: "long"
}, },
onMounted(){ onMounted(){
this.start(); this.start();
}, },
start(){ start(){
setTimeout(()=>{ setTimeout(()=>{
this.makeString(); this.makeString();
this.start(); this.start();
},200); },200);
}, },
makeString(){ makeString(){
if(this.state.format=="long"){ if(this.state.format=="long"){
this.update({time_text:dayjs().format("MMMM D, YYYY h:mm:ss A")}); this.update({time_text:dayjs().format("MMMM D, YYYY h:mm:ss A")});
} }
} }
}; };
</script> </script>
</timestamp> </timestamp>