Compare commits

..

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

17 changed files with 515 additions and 5763 deletions

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
```

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,8 +1,8 @@
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){},
} }
@ -16,14 +16,14 @@ const InterfaceFile = {
} }
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){}
} }

View File

@ -3,61 +3,22 @@ const {InterfaceRecord,InterfaceFile,InterfaceTag,InterfaceTagLink} = require(".
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,
"created_at" INTEGER NOT NULL,
"updated_at" INTEGER NOT NULL,
PRIMARY KEY("tag")
);`;
const table_idx = ``;
if(!db){
throw new Error("Database is not connected");
}
db.exec(table + "\n" + table_idx);
this.db = db;
} }
get table_add(){ add(uuid,word){
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(){ delete(uuid){
if(!this._table_delete){
this._table_delete = db.prepare("DELETE FROM tags WHERE tag = ?");
}
return this._table_delete;
}
add(tag){
return new Promise(resolve=>{
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);
});
} }
} }
@ -65,91 +26,14 @@ class TimeChainDataSqliteTag extends Interface(InterfaceTag) {
class TimeChainDataSqliteTagLink extends Interface(InterfaceTagLink) { class TimeChainDataSqliteTagLink extends Interface(InterfaceTagLink) {
constructor(){ 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 = `
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(!_table_add){
this._table_add = db.prepare('INSERT INTO taglink (uuid,tag,created_at) VALUES (?,?,?)');
}
return this._table_add;
}
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_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_record(){
if(!this._table_delete_record){
this._table_delete_record = db.prepare('DELETE FROM taglink WHERE uuid=?');
}
}
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);
});
}
delete(uuid,tag){
return new Promise(resolve=>{
const prepare = this.table_delete;
const res = prepare.run(uuid,tag);
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);
});
}
deleteRecord(uuid){
return new Promise(resolve=>{
const prepare = this.table_delete_record;
const res = prepare.run(uuid);
return resolve(res?.changes);
});
}
} }
class TimeChainDataSqliteFile extends Interface(InterfaceFile) { class TimeChainDataSqliteFile extends Interface(InterfaceFile) {
constructor(){ constructor(){
super();
const table = `CREATE TABLE IF NOT EXISTS "files" ( const table = `CREATE TABLE IF NOT EXISTS "files" (
"uuid" TEXT UNIQUE, "uuid" TEXT UNIQUE,
"uuid_record" TEXT NOT NULL, "uuid_record" TEXT NOT NULL,
@ -224,7 +108,7 @@ class TimeChainDataSqliteFile extends Interface(InterfaceFile) {
add(uuid_record,uuid,timestamp,content,mime,hash){ add(uuid_record,uuid,timestamp,content,mime,hash){
return new Promise(resolve=>{ return new Promise(resolve=>{
const dt = Math.floor(Date.now()); const dt = Math.floor(Date.now());
const res = this.table_insert.run(uuid,uuid_record,timestamp,content,mime,hash,dt,dt); const res = this.table_insert.exec(uuid,uuid_record,timestamp,content,mime,hash,dt,dt);
return resolve(res?.changes); return resolve(res?.changes);
}); });
} }
@ -245,22 +129,21 @@ class TimeChainDataSqliteFile extends Interface(InterfaceFile) {
delete(uuid){ delete(uuid){
return new Promise(resolve=>{ return new Promise(resolve=>{
const prepare = this.table_delete; const res = this.table_delete.exec(uuid);
const res = prepare.exec(uuid);
return resolve(res?.changes); return resolve(res?.changes);
}) })
} }
deleteRecord(uuid_record){ deleteRecord(){
return new Promise(resolve=>{ return new Promise(resolve=>{
const res = this.table_delete_record.run(uuid_record); const res = this.table_delete_record.exec(uuid_record);
return resolve(res?.changes); return resolve(res?.changes);
}) })
} }
update(uuid,timestamp,content,mime,hash){ update(uuid,timestamp,content,mime,hash){
return new Promise(resolve=>{ return new Promise(resolve=>{
const res = this.table_update.run(timestamp,content,mime,hash,uuid); const res = this.table_update.exec(timestamp,content,mime,hash,uuid);
return resolve(res?.changes); return resolve(res?.changes);
}) })
} }
@ -269,7 +152,6 @@ class TimeChainDataSqliteFile extends Interface(InterfaceFile) {
class TimeChainDataSqliteRecord extends Interface(InterfaceRecord) { class TimeChainDataSqliteRecord extends Interface(InterfaceRecord) {
constructor(){ constructor(){
super();
//TODO: Create Tables Here if not exist //TODO: Create Tables Here if not exist
const table = `CREATE TABLE IF NOT EXISTS "records" ( const table = `CREATE TABLE IF NOT EXISTS "records" (
"uuid" TEXT NOT NULL UNIQUE, "uuid" TEXT NOT NULL UNIQUE,
@ -359,8 +241,7 @@ class TimeChainDataSqliteRecord extends Interface(InterfaceRecord) {
delete(uuid){ delete(uuid){
return new Promise(resolve=>{ return new Promise(resolve=>{
const prepare = this.table_delete; const res = this.table_delete.exec(uuid);
const res = prepare.run(uuid);
return resolve(res?.changes); return resolve(res?.changes);
}); });
} }
@ -368,7 +249,7 @@ class TimeChainDataSqliteRecord extends Interface(InterfaceRecord) {
update(uuid,content,mime,hash){ update(uuid,content,mime,hash){
return new Promise(resolve=>{ return new Promise(resolve=>{
const dt = Math.floor(Date.now()); const dt = Math.floor(Date.now());
const res = this.table_update.run(content,mime,hash,dt,uuid); const res = this.table_update.exec(content,mime,hash,dt,uuid);
return resolve(res?.changes); return resolve(res?.changes);
}) })
} }
@ -379,14 +260,5 @@ class TimeChainDataSqlite {
} }
module.exports = {
ConnectToDatabase,
TimeChainDataSqlite,
TimeChainDataSqliteRecord,
TimeChainDataSqliteFile,
TimeChainDataSqliteTagLink,
TimeChainDataSqliteTag
}

View File

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

View File

View File

@ -1,4 +1,4 @@
import Timestamp from './src/ui/timestamp.riot' import Timestamp from './timestamp.riot'
import { component } from 'riot' import { component } from 'riot'

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.