diff --git a/.gitignore b/.gitignore index f4166cb..5801f24 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ node_modules -uploads/* -database.sqlite* \ No newline at end of file +app/uploads/* +app/tmp +database.sqlite* +*.bak +package-lock.json \ No newline at end of file diff --git a/app.js b/app/app.js similarity index 74% rename from app.js rename to app/app.js index 58807b6..df7179d 100644 --- a/app.js +++ b/app/app.js @@ -1,5 +1,9 @@ port = 8000; +var log = require('loglevel'); + +log.setLevel(log.levels.INFO); + // Require the libraries: var SocketIOFileUpload = require('socketio-file-upload'), socketio = require('socket.io'), @@ -11,10 +15,31 @@ const app = express() const http = require('http'); const server = http.createServer(app); const { Server } = require("socket.io"); -const { SqliteHandler, UploadObject } = require('./handlers/sqliteHandler'); -var fs = require('fs') +const { SqliteHandler } = require('./handlers/sqliteHandler'); +const { UploadObject } = require('./handlers/uploadHandler'); + +// Prepare directories +var fs = require('fs'); +var path = require('path'); + +tmp = __dirname + "/tmp/"; +uploadsdir = __dirname + "/uploads/"; + +if (!fs.existsSync(tmp)){ + fs.mkdirSync(tmp); +} else { + for (const file of fs.readdirSync(tmp)) { + fs.unlinkSync(path.join(tmp, file)); + } +} + +if (!fs.existsSync(uploadsdir)){ + fs.mkdirSync(uploadsdir); +} + const io = new Server(server); + sqliteobj = new SqliteHandler("database.sqlite"); db = sqliteobj.getDatabaseObj(); @@ -32,19 +57,38 @@ app.get('/api/download/:hash', (req, res) => { if(handler == undefined) { var handler = new UploadObject(db); } - handler.load(req.params.hash); - if(handler.isLoaded()){ + if(handler.load(req.params.hash)){ res.status(200).json({result: { files: handler.getFiles() }, code:200}) } }) +app.get('/download/:hash/index/:index', (req, res) => { + if(handler == undefined) { + var handler = new UploadObject(db); + } + if(handler.load(req.params.hash) && typeof parseInt(req.params.index) == 'number') { + res.set("Content-Disposition", "attachment; filename=\""+handler.getFiles()[req.params.index].filename+"\"") + res.sendFile(__dirname + "/uploads/" + handler.getHash()+"/"+handler.getFiles()[req.params.index].filename); + } else { + notFound(req, res); + } +}) + app.get('/upload', (req, res) => { res.sendFile(__dirname + "/forms/upload.html"); }) -app.get('/download/(*)', (req, res) => { +app.get('/download', (req, res) => { + res.sendFile(__dirname + "/forms/download.html"); +}) + +app.get('/download/:hash', (req, res) => { + res.sendFile(__dirname + "/forms/download.html"); +}) + +app.get('/download/:hash/:adminhash', (req, res) => { res.sendFile(__dirname + "/forms/download.html"); }) @@ -72,38 +116,58 @@ app.get('/download/(*)', (req, res) => { // var io = new socketio.Server(app.listen(port)); io.sockets.on("connection", function(socket){ var handler = new UploadObject(db); - console.log(handler); if(socket.handshake.url.startsWith("/socket.io")) { var uploader = new SocketIOFileUpload(); uploader.dir = "tmp"; + + uploader.listen(socket); + var processed = 0; + socket.on("prepareUpload", function(num_files){ + console.log("Got \"prepareUpload\" event"); + console.log(num_files," Files") + if(handler.new(num_files)){ + console.log("Emitting \"serverReady\" event"); + socket.emit("serverReady"); + } + } + ) + uploader.uploadValidator = function(event, callback){ // asynchronous operations allowed here; when done, - if (true) { + if (handler.isLoaded()) { callback(true); } else { callback(false); } }; - uploader.listen(socket); + uploader.on("start", function(event){ // console.log(handler); - handler.new(); + }); // Do something when a file is saved: uploader.on("saved", function(event){ - if (event.file.success) { - fs.renameSync(event.file.pathName, "uploads/"+handler.getDir()+"/"+event.file.name) - handler.registerFile(event.file.name); + if (handler.isLoaded()) { + processed++; + if (event.file.success) { + fs.renameSync(event.file.pathName, "uploads/"+handler.getDir()+"/"+event.file.name) + handler.registerFile(event.file.name); + } else { + fs.unlinkSync(event.file.pathName); + } + console.log("Process Counter Updated", processed); + console.log(handler.getNumFiles()); + if (handler.getNumFiles() == processed && handler.getNumFiles() > 0) { + socket.emit("linkCreated", handler.getHash(), handler.getAdminHash()); + } } - console.log(event.file); - }); uploader.on("complete", function(event){ - socket.emit("linkCreated", handler.getHash(), handler.getAdminHash()); + }); // Error handler: uploader.on("error", function(event){ @@ -147,7 +211,7 @@ app.use(function (req, res, next) { server.listen(port, () => { - console.log('listening on *:8000'); + log.info("Server listening on *:"+port); }); diff --git a/forms/select.html b/app/forms/select.html similarity index 92% rename from forms/select.html rename to app/forms/select.html index 4a9abd6..26f1998 100644 --- a/forms/select.html +++ b/app/forms/select.html @@ -12,10 +12,10 @@
-
-
-
Upload
-
Download
+
+
+
Upload

New Upload

+
Download

Download

diff --git a/app/forms/upload.html b/app/forms/upload.html new file mode 100644 index 0000000..0d53f71 --- /dev/null +++ b/app/forms/upload.html @@ -0,0 +1,98 @@ + + + + + + + + Kakubovna: Upload + + + + + +
+
+
+ +
+

Drag and drop your files
or
Click to add

+
+
+
+
+
+ +
+
+ + + + + + \ No newline at end of file diff --git a/app/handlers/sqliteHandler.js b/app/handlers/sqliteHandler.js new file mode 100644 index 0000000..2164cde --- /dev/null +++ b/app/handlers/sqliteHandler.js @@ -0,0 +1,44 @@ +module.exports = { + + SqliteHandler: class SqliteHandler { + constructor(path) { + this.loaded = false; + this.path = path; + this.db = require('better-sqlite3')(this.path); + this.db.prepare(`CREATE TABLE IF NOT EXISTS "user" ( + "id" INTEGER NOT NULL UNIQUE, + "name" TEXT NOT NULL UNIQUE, + "password" TEXT NOT NULL, + "email" INTEGER, + "admin" INTEGER, + PRIMARY KEY("id" AUTOINCREMENT) + )`).run(); + + this.db.prepare(`CREATE TABLE IF NOT EXISTS "upload" ( + "id" INTEGER NOT NULL UNIQUE, + "hashsalt" TEXT NOT NULL UNIQUE, + "adminhash" TEXT NOT NULL UNIQUE, + "desc" TEXT, + "password" TEXT, + "owner_id" INTEGER, + "upload_unix" INTEGER, + PRIMARY KEY("id" AUTOINCREMENT), + FOREIGN KEY("owner_id") REFERENCES "user"("id") + )`).run(); + + this.db.prepare(`CREATE TABLE IF NOT EXISTS "file" ( + "id" INTEGER NOT NULL UNIQUE, + "filename" TEXT NOT NULL, + "upload_id" INTEGER NOT NULL, + "state" INTEGER NOT NULL, + PRIMARY KEY("id" AUTOINCREMENT), + FOREIGN KEY("upload_id") REFERENCES "upload"("id") + )`).run(); + } + + getDatabaseObj() { + return this.db; + } + } + +} \ No newline at end of file diff --git a/handlers/sqliteHandler.js b/app/handlers/uploadHandler.js similarity index 67% rename from handlers/sqliteHandler.js rename to app/handlers/uploadHandler.js index ee4116d..3463968 100644 --- a/handlers/sqliteHandler.js +++ b/app/handlers/uploadHandler.js @@ -5,25 +5,33 @@ module.exports = { this.db = db; this.absrc = ['E', 'T', 'X', 'W', 'U', 'R', 'J', 'I', 'N', 'C']; this.loaded = false; + this.num_files = 0; this.fileCache = {}; } - new() { + new(num_files) { if (!this.loaded) { + if (typeof num_files == "number" && num_files > 0) { + this.num_files = num_files; + } else { + console.error("Invalid number of files"); + return false; + } this.hashsalt = this.makeHash(10); this.adminhash = this.makeHash(10); var sql = this.db.prepare(`INSERT INTO "upload" (hashsalt, adminhash, upload_unix) VALUES (?, ?, ?)`); var result = sql.run(this.hashsalt, this.adminhash, Math.floor(Date.now() / 1000)); this.id = result.lastInsertRowid; this.hashid = this.id2hashid(result.lastInsertRowid); - this.dir = this.hashid+this.hashsalt; + this.dir = this.hashid + this.hashsalt; var fs = require('fs'); - - if (!fs.existsSync("uploads/"+this.dir)){ - fs.mkdirSync("uploads/"+this.dir); + + if (!fs.existsSync("uploads/" + this.dir)) { + fs.mkdirSync("uploads/" + this.dir); } this.loaded = true; + return true; } } @@ -33,17 +41,37 @@ module.exports = { this.hashid = shash[0]; this.id = this.hashid2id(this.hashid) this.hashsalt = shash[1]; - this.dir = this.hashid+this.hashsalt; - + this.dir = this.hashid + this.hashsalt; + + var sql = this.db.prepare(`SELECT count(id) AS "count" FROM "file" WHERE upload_id=?`); + var num_files = sql.get(this.id)["count"]; + if (typeof num_files == "number" && num_files > 0) { + this.num_files = num_files; + } else { + console.error("Invalid number of files"); + return false; + } + var sql = this.db.prepare(`SELECT EXISTS(SELECT 1 FROM "upload" WHERE id=? AND hashsalt=? LIMIT 1) AS "exists"`); var result = sql.get(this.id, this.hashsalt); + + + if (result["exists"]) { console.log("LOADED: ", result); this.loaded = true; + return true; + } else { + console.log("Not Found"); + return false; } } } - + + getNumFiles() { + return this.num_files; + } + isLoaded() { return this.loaded; } @@ -62,6 +90,10 @@ module.exports = { return this.files; } + getFileByIndex(index) { + return this.getFiles()[index]; + } + registerFile(filename) { var sql = this.db.prepare(`INSERT INTO "file" (filename, upload_id) VALUES (?, ?)`); var result = sql.run(filename, this.id); @@ -73,9 +105,9 @@ module.exports = { } getHash() { - return this.hashid+this.hashsalt; + return this.hashid + this.hashsalt; } - + getHashId() { return this.hashid; } @@ -122,47 +154,5 @@ module.exports = { return [hash.slice(0, hash.length - 10), hash.slice(hash.length - 10, hash.length)] } - }, - - SqliteHandler: class SqliteHandler { - constructor(path) { - this.loaded = false; - this.path = path; - this.db = require('better-sqlite3')(this.path); - this.db.prepare(`CREATE TABLE IF NOT EXISTS "user" ( - "id" INTEGER NOT NULL UNIQUE, - "name" TEXT NOT NULL UNIQUE, - "password" TEXT NOT NULL, - "email" INTEGER, - "admin" INTEGER, - PRIMARY KEY("id" AUTOINCREMENT) - )`).run(); - - this.db.prepare(`CREATE TABLE IF NOT EXISTS "upload" ( - "id" INTEGER NOT NULL UNIQUE, - "hashsalt" TEXT NOT NULL UNIQUE, - "adminhash" TEXT NOT NULL UNIQUE, - "desc" TEXT, - "password" TEXT, - "owner_id" INTEGER, - "upload_unix" INTEGER, - PRIMARY KEY("id" AUTOINCREMENT), - FOREIGN KEY("owner_id") REFERENCES "user"("id") - )`).run(); - - this.db.prepare(`CREATE TABLE IF NOT EXISTS "file" ( - "id" INTEGER NOT NULL UNIQUE, - "filename" TEXT NOT NULL, - "upload_id" INTEGER NOT NULL, - "state" INTEGER NOT NULL, - PRIMARY KEY("id" AUTOINCREMENT), - FOREIGN KEY("upload_id") REFERENCES "upload"("id") - )`).run(); - } - - getDatabaseObj() { - return this.db; - } } - } \ No newline at end of file diff --git a/package.json b/app/package.json similarity index 92% rename from package.json rename to app/package.json index dd1b69d..97e0e78 100644 --- a/package.json +++ b/app/package.json @@ -11,7 +11,7 @@ "dependencies": { "better-sqlite3": "^8.4.0", "express": "^4.18.2", - "multer": "^1.4.5-lts.1", + "loglevel": "^1.8.1", "socket.io": "^4.6.2", "socketio-file-upload": "^0.7.3" } diff --git a/app/public/css/style.css b/app/public/css/style.css new file mode 100644 index 0000000..9fce8df --- /dev/null +++ b/app/public/css/style.css @@ -0,0 +1,174 @@ +@charset "UTF-8"; +html, +body { + padding: 0; + margin: 0; + border: none; +} + +body { + background-color: #1b1b1b; +} + +canvas { + display: block; +} + +.box__dragndrop, +.box__uploading, +.box__success, +.box__error { + display: none; +} + +.flex-container { + display: flex; + align-items: center; + justify-content: center; + height: 100vh; +} + +.container { + font-family: "Geologica", sans-serif; + border-radius: 15px; + border-color: black; + border-width: 2px; + border-style: solid; + background-color: #f16d01; + padding: 20px; + display: flex; + text-align: center; +} + +.vertical { + flex-direction: column; + justify-content: center; + align-items: center; +} + +.list { + margin-top: 30px; +} + +.filelist { + display: flex; + flex-direction: column; + row-gap: 0; +} + +.filelist p { + color: #FFFFFF; +} + +.list p { + color: #FFFFFF; +} + +#select { + flex-direction: column; +} + +.button { + display: inline-block; + border-radius: 4px; + background-color: #e42200; + border: none; + color: #FFFFFF; + text-align: center; + font-size: 28px; + padding: 20px; + width: 200px; + transition: all 0.5s; + cursor: pointer; + margin: 5px; +} + +.button span { + cursor: pointer; + display: inline-block; + position: relative; + transition: 0.5s; +} + +.button span:after { + content: "ยป"; + position: absolute; + opacity: 0; + top: 0; + right: -20px; + transition: 0.5s; +} + +.button:hover span { + padding-right: 25px; +} + +.button:hover span:after { + opacity: 1; + right: 0; +} + +.card-button-box { + display: flex; + justify-content: center; + gap: 50px; +} +.card-button-box .card-button { + display: flex; + background-color: rgba(88, 88, 88, 0.2); + text-align: center; + padding: 10px; + height: 100px; + width: 400px; + border-radius: 15px; + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2); + transition: 0.3s; + flex-direction: row; + justify-content: center; + cursor: pointer; +} +.card-button-box .card-button img { + height: 100%; + flex-basis: 40%; +} +.card-button-box .card-button p { + flex-basis: 60%; + color: #FFFFFF; + text-align: left; + font-size: xx-large; +} + +.card-button:hover { + transform: scale(1.02); + box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2); +} + +.dropZoneContainer { + display: grid; + margin-left: 100px; + margin-right: 100px; + width: 100%; + height: 100px; +} + +.dropZoneOverlay, .FileUpload { + height: 100%; + width: 100%; + grid-column: 1; + grid-row: 1; +} + +.dropZoneOverlay { + margin-top: -3px; + margin-left: -3px; + border: dotted 3px; + font-size: 110%; + color: #FFFFFF; + text-align: center; + vertical-align: middle; +} + +.FileUpload { + opacity: 0; + cursor: pointer; +}/*# sourceMappingURL=style.css.map */ \ No newline at end of file diff --git a/app/public/css/style.css.map b/app/public/css/style.css.map new file mode 100644 index 0000000..14df6bf --- /dev/null +++ b/app/public/css/style.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["style.css","style.scss"],"names":[],"mappings":"AAAA,gBAAgB;ACAhB;;EAEE,UAAA;EACA,SAAA;EACA,YAAA;ADEF;;ACEA;EACE,yBAAA;ADCF;;ACGA;EACE,cAAA;ADAF;;ACGA;;;;EAIE,aAAA;ADAF;;ACGA;EACE,aAAA;EACA,mBAAA;EACA,uBAAA;EACA,aAAA;ADAF;;ACGA;EACE,oCAAA;EACA,mBAAA;EACA,mBAAA;EACA,iBAAA;EACA,mBAAA;EACA,yBAAA;EACA,aAAA;EACA,aAAA;EACA,kBAAA;ADAF;;ACGA;EACI,sBAAA;EACA,uBAAA;EACA,mBAAA;ADAJ;;ACGA;EACI,gBAAA;ADAJ;;ACGA;EACI,aAAA;EACA,sBAAA;EACA,UAAA;ADAJ;;ACEA;EACI,cAAA;ADCJ;;ACEA;EACI,cAAA;ADCJ;;ACEA;EACI,sBAAA;ADCJ;;ACEA;EACI,qBAAA;EACA,kBAAA;EACA,yBAAA;EACA,YAAA;EACA,cAAA;EACA,kBAAA;EACA,eAAA;EACA,aAAA;EACA,YAAA;EACA,oBAAA;EACA,eAAA;EACA,WAAA;ADCJ;;ACEA;EACI,eAAA;EACA,qBAAA;EACA,kBAAA;EACA,gBAAA;ADCJ;;ACEA;EACI,YAAA;EACA,kBAAA;EACA,UAAA;EACA,MAAA;EACA,YAAA;EACA,gBAAA;ADCJ;;ACEA;EACI,mBAAA;ADCJ;;ACEA;EACI,UAAA;EACA,QAAA;ADCJ;;ACEA;EACI,aAAA;EACA,uBAAA;EACA,SAAA;ADCJ;ACAI;EACI,aAAA;EACA,uCAAA;EACA,kBAAA;EACA,aAAA;EACA,aAAA;EACA,YAAA;EACA,mBAAA;EACA,0CAAA;EACA,gBAAA;EACA,mBAAA;EACA,uBAAA;EACA,eAAA;ADER;ACDQ;EACI,YAAA;EACA,eAAA;ADGZ;ACAQ;EACI,eAAA;EACA,cAAA;EACA,gBAAA;EACA,mBAAA;ADEZ;;ACOA;EACI,sBAAA;EACA,2CAAA;ADJJ;;ACOA;EACI,aAAA;EACA,kBAAA;EACA,mBAAA;EACA,WAAA;EACA,aAAA;ADJJ;;ACOA;EACI,YAAA;EACA,WAAA;EACA,cAAA;EACA,WAAA;ADJJ;;ACOA;EACI,gBAAA;EACA,iBAAA;EACA,kBAAA;EACA,eAAA;EACA,cAAA;EACA,kBAAA;EACA,sBAAA;ADJJ;;ACMA;EACI,UAAA;EACA,eAAA;ADHJ","file":"style.css"} \ No newline at end of file diff --git a/public/css/style.css b/app/public/css/style.scss similarity index 76% rename from public/css/style.css rename to app/public/css/style.scss index 0d8d9f6..0b704ae 100644 --- a/public/css/style.css +++ b/app/public/css/style.scss @@ -31,7 +31,7 @@ canvas { .container { font-family: 'Geologica', sans-serif; - border-radius: 4px; + border-radius: 15px; border-color: black; border-width: 2px; border-style: solid; @@ -112,29 +112,42 @@ canvas { display: flex; justify-content: center; gap: 50px; + .card-button { + display: flex; + background-color: rgba(88, 88, 88, 0.2); + text-align: center; + padding: 10px; + height: 100px; + width: 400px; + border-radius: 15px; + box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); + transition: 0.3s; + flex-direction: row; + justify-content: center; + cursor: pointer; + img { + height: 100%; + flex-basis: 40%; + } + + p { + flex-basis: 60%; + color:#FFFFFF; + text-align: left; + font-size: xx-large; + } + + } } -.card-button { - background-color: rgba(88, 88, 88, 0.2); - text-align: center; - padding: 5px; - width: 200px; - border-radius: 15px; - box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); - transition: 0.3s; -} + + .card-button:hover { transform: scale(1.02); box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2); } -.card-button p { - position: absolute; - color:#FFFFFF; - font-size: x-large; -} - .dropZoneContainer { display: grid; margin-left: 100px; @@ -161,4 +174,5 @@ canvas { } .FileUpload { opacity: 0; + cursor: pointer; } \ No newline at end of file diff --git a/public/img/add-sharp-white.svg b/app/public/img/add-sharp-white.svg similarity index 100% rename from public/img/add-sharp-white.svg rename to app/public/img/add-sharp-white.svg diff --git a/public/img/arrow-down-white.svg b/app/public/img/arrow-down-white.svg similarity index 100% rename from public/img/arrow-down-white.svg rename to app/public/img/arrow-down-white.svg diff --git a/public/js/siofu-client.js b/app/public/js/siofu-client.js similarity index 100% rename from public/js/siofu-client.js rename to app/public/js/siofu-client.js diff --git a/public/js/socket.io.js b/app/public/js/socket.io.js similarity index 100% rename from public/js/socket.io.js rename to app/public/js/socket.io.js diff --git a/public/script.js b/app/public/script.js similarity index 100% rename from public/script.js rename to app/public/script.js diff --git a/forms/download.html b/forms/download.html deleted file mode 100644 index 57fa9a1..0000000 --- a/forms/download.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - Kakubovna: Download - - - - - -
- - - -
- - - - \ No newline at end of file diff --git a/handlers/fileHandler.js b/handlers/fileHandler.js deleted file mode 100644 index e69de29..0000000 diff --git a/handlers/userHandler.js b/handlers/userHandler.js deleted file mode 100644 index e69de29..0000000 diff --git a/public/img/add-sharp.svg b/public/img/add-sharp.svg deleted file mode 100644 index d4e12e4..0000000 --- a/public/img/add-sharp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/img/arrow-down.svg b/public/img/arrow-down.svg deleted file mode 100644 index f6027be..0000000 --- a/public/img/arrow-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file