]> git.ardis.lu - sheets-db/commitdiff
Implement uploading .sqlite3 to Backblaze
authorArdis Lu <ardisglu@gmail.com>
Mon, 29 Nov 2021 03:38:44 +0000 (19:38 -0800)
committerArdis Lu <ardisglu@gmail.com>
Mon, 29 Nov 2021 03:38:44 +0000 (19:38 -0800)
Code.gs
query-to-db.html

diff --git a/Code.gs b/Code.gs
index bf5ac8dcfe49535fec7d561c96e5c15abc6120e2..b973178eb4e3d51f876ab0a364aac3a191da8e77 100644 (file)
--- a/Code.gs
+++ b/Code.gs
@@ -2,7 +2,6 @@ const UI = SpreadsheetApp.getUi();
 const WORKBOOK = SpreadsheetApp.getActive();
 
 const PROPERTIES = PropertiesService.getScriptProperties();
-const FILE_ID = PROPERTIES.getProperty('FILE_ID');
 const BACKBLAZE_ID = PROPERTIES.getProperty('BACKBLAZE_ID');
 const BACKBLAZE_KEY = PROPERTIES.getProperty('BACKBLAZE_KEY');
 
@@ -10,7 +9,6 @@ function onOpen() {
   UI.createMenu('🔥 sheets-db')
     .addItem('❓ Help', 'showHelp')
     .addItem('🟢 Open sidebar', 'openSidebar')
-    .addItem('Upload to Backblaze', 'uploadToBackblaze')
     .addToUi();
 }
 
@@ -101,7 +99,8 @@ function generateDatabaseQuery() {
   return query;
 }
 
-function uploadToBackblaze() {
+// Helper function which will be called from the client-side dialog
+function uploadToBackblaze(byteArray) {
   // b2_authorize_account
   const credentials = `Basic ${Utilities.base64Encode(`${BACKBLAZE_ID}:${BACKBLAZE_KEY}`)}`;
   const authOptions = {
@@ -131,12 +130,9 @@ function uploadToBackblaze() {
   const uploadUrl = getUploadUrlJson['uploadUrl'];
 
   // b2_upload_file
-  const file = DriveApp.getFileById(FILE_ID);
-  const fileName = file.getName();
-  const mimeType = 'text/plain'; // Use 'text/plain' to enable compression by CDN. file.getMimeType() gives 'application/octet-stream', which will not be compressed.
-  const contentLength = file.getSize();
-  const data = file.getBlob();
-  const digest = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_1, data.getBytes());
+  const file = Utilities.newBlob(byteArray, 'application/vnd.sqlite3', 'database.sqlite3');
+  const fileSize = byteArray.length;
+  const digest = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_1, file.getBytes());
   let sha1 = '';
   for (let byte of digest) {
     if (byte < 0) {
@@ -149,14 +145,12 @@ function uploadToBackblaze() {
     'method': 'POST',
     'headers': {
       'Authorization': authToken,
-      'X-Bz-File-Name': fileName,
-      'Content-Type': mimeType,
+      'X-Bz-File-Name': file.getName(),
+      'Content-Type': file.getContentType(),
       'X-Bz-Content-Sha1': sha1
     },
-    'contentLength': contentLength,
-    'payload': data
+    'contentLength': fileSize,
+    'payload': file
   }
   const uploadResponse = UrlFetchApp.fetch(uploadUrl, uploadOptions);
-
-  UI.alert('Success!', 'Google Drive file was successfully uploaded to Backblaze.', UI.ButtonSet.OK);
 }
index d42378fd1a93e51d6fda6b7a9c63a5bd99f49e5f..bd4e7d3553ca025a677a47cd102860153aa30533 100644 (file)
 </head>
 
 <body>
-  <p id="status">Initializing database...</p>
+  <p id="status"></p>
 
   <button id="generate-button" disabled>Generate SQLite database</button>
   <span id="generate-spinner" class="inline-spinner"></span>
+  <p id="generate-notification"></p>
   <a id="download-link"></a>
 
+  <button id="upload-button" disabled>Upload .sqlite3 file to Backblaze</button>
+  <span id="upload-spinner" class="inline-spinner"></span>
+  <p id="upload-notification"></p>
+
   <script src="https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.6.1/sql-wasm.min.js"></script>
   <script>
-    let SQL;
-    let db;
+    // Globals for SQLite which will be overwritten later
+    let SQL; // the sql.js instance
+    let db; // the currently active SQLite database
+    let file; // the exported .sqlite3 database file
+
+    // Globals mapping to each element
     const status = document.getElementById('status');
+
     const generateButton = document.getElementById('generate-button');
     const generateSpinner = document.getElementById('generate-spinner');
-    generateSpinner.style.display = 'none';
+    const generateNotification = document.getElementById('generate-notification');
     const downloadLink = document.getElementById('download-link');
 
-    const generateHandler = () => {
-      status.textContent = 'Generating database...';
-      generateButton.disabled = true;
-      generateSpinner.style.display = '';
-      downloadLink.textContent = '';
+    const uploadButton = document.getElementById('upload-button');
+    const uploadSpinner = document.getElementById('upload-spinner');
+    const uploadNotification = document.getElementById('upload-notification');
+
+    const setState = state => {
+      const mapping = {
+        'initial': ['Initializing database...', true, 'none', '', '', true, 'none', ''],
+        'loaded': ['SQLite loaded. Ready to generate database!', false, 'none', '', '', true, 'none', ''],
+        'generatingDatabase': ['Generating database...', true, '', '', '', true, 'none', uploadNotification.textContent],
+        'downloadReady': ['Ready!', false, 'none', `Database generated: ${(new Date()).toLocaleString()}`, 'Click here to download the .sqlite3 database.', false, 'none', uploadNotification.textContent],
+        'uploadingDatabase': ['Uploading .sqlite3 file to Backblaze...', true, 'none', generateNotification.textContent, downloadLink.textContent, true, '', uploadNotification.textContent],
+        'uploadComplete': ['Ready!', false, 'none', generateNotification.textContent, downloadLink.textContent, false, 'none', `Uploaded to Backblaze: ${(new Date()).toLocaleString()}`]
+      };
+
+      const content = mapping[state];
+
+      status.textContent = content[0];
+      generateButton.disabled = content[1];
+      generateSpinner.style.display = content[2];
+      generateNotification.textContent = content[3];
+      downloadLink.textContent = content[4];
+      uploadButton.disabled = content[5];
+      uploadSpinner.style.display = content[6];
+      uploadNotification.textContent = content[7];
+    }
+
+    setState('initial');
+
+    // Call the server-side generateDatabaseQuery, then create the SQLite database
+    generateButton.addEventListener('click', () => {
+      setState('generatingDatabase');
 
       db.close(); // Must be called to free memory from previous databases
       db = new SQL.Database();
 
-      const queryHandler = query => {
+      google.script.run.withSuccessHandler(query => {
         db.run(query);
         const buffer = db.export();
-        const file = new Blob([buffer], { type: 'application/vnd.sqlite3' });
+        file = new File([buffer], 'database.sqlite3', { type: 'application/vnd.sqlite3' });
 
         downloadLink.setAttribute('download', `${Date.now()}.sqlite3`);
         downloadLink.setAttribute('href', `${URL.createObjectURL(file)}`);
-        downloadLink.textContent = 'Click here to download the .sqlite3 database.';
+        setState('downloadReady');
+      }).generateDatabaseQuery();
+    });
 
-        status.textContent = `Last database generation: ${(new Date()).toLocaleString()}`;
-        generateButton.disabled = false;
-        generateSpinner.style.display = 'none';
-      }
+    // Convert the SQLite database into a byte array, then send it to the server-side uploadToBackblaze
+    uploadButton.addEventListener('click', async () => {
+      setState('uploadingDatabase');
 
-      google.script.run.withSuccessHandler(queryHandler).generateDatabaseQuery();
-    }
+      const buffer = await file.arrayBuffer();
+      const byteArray = [...new Uint8Array(buffer)]; // Need to spread into a non-typed array to pass to server-side
 
-    generateButton.addEventListener('click', generateHandler);
+      google.script.run.withSuccessHandler(() => {
+        setState('uploadComplete');
+      }).uploadToBackblaze(byteArray);
+    });
 
+    // Initialize sql.js
     (async () => {
       // WARNING: The following line will break if you try to use string interpolation (i.e. strings with backticks)
       SQL = await initSqlJs({ locateFile: file => 'https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.6.1/' + file });
       db = new SQL.Database();
 
-      status.textContent = 'SQLite loaded. Ready to generate database!';
-      generateButton.disabled = false;
+      setState('loaded');
     })();
   </script>
 </body>
This page took 0.048457 seconds and 4 git commands to generate.