./src/models.js annotated source

Back to index

        

This file contains all logic for managing the filesystem backed database of Codeframe files.

3
4const fs = require('fs');
5const path = require('path');
6const crypto = require('crypto');
7const zlib = require('zlib');
8
9const config = require('../config.js');
10

These files are Codeframe files that must be ensured to exist in the database with every deploy, since they're used in demos on the home page. These are checked for existence later.

14const STARTER_FIXTURES = [
15    'blank-torus.js',
16    'blank.frame',
17    'button-effects.html',
18    'canvas.js',
19    'filters-shadows.html',
20    'flexbox.html',
21    'helloworld.html',
22    'helloworld.js',
23    'simple-blog.html',
24    'interactive-input.html',
25    'interactive-input.js',
26    'nametag-torus.js',
27    'see-javascript.html',
28    'todo-torus.js',
29    'welcome.html',
30];
31

Utility method to get a trimmed sha256 hash of a string.

33const hashFile = contents => {
34    const hash = crypto.createHash('sha256');
35    hash.update(contents);
36    // first 12 chars of the hex digest
37    return hash.digest('hex').substr(0, 12);
38}
39

SourceFileStore is the database that manages the app's communication with the filesystem-backed storage for Codeframe files. For efficiency of data in storage, we compress files stored here with gzip for on-disk storage.

43class SourceFileStore {
44
45    constructor(basePath) {
46        this.basePath = basePath;
47        if (!fs.existsSync(this.basePath)) {
48            fs.mkdirSync(this.basePath);
49        }

The first time the file store is created, we make sure each of the required demo snippets exists.

52        for (const fxt of STARTER_FIXTURES) {
53            fs.readFile(`starter_fixtures/${fxt}`, 'utf8', (err, data) => {
54                if (err) {
55                    console.error(err);
56                } else {
57                    this.create(data);
58                }
59            });
60        }
61    }
62
63    getPathFromHash(hash) {
64        return path.join(this.basePath, `cf_${hash}.frame`);
65    }
66
67    getHashedFilePath(contents) {
68        return this.getPathFromHash(hashFile(contents));
69    }
70
71    has(sourceFilePath) {
72        return new Promise((res, _rej) => {
73            fs.access(sourceFilePath, fs.constants.R_OK, err => {
74                res(!err);
75            });
76        });
77    }
78

Given a hash, returns a Promise resolving to the contents of the file, or rejects.

80    getFromFS(frameHash) {
81        return new Promise((res, rej) => {
82            fs.readFile(this.getPathFromHash(frameHash), (err, data) => {
83                if (err) {
84                    rej(err);
85                } else {

unzip gzip compression of the read data before returning to the caller

88                    zlib.gunzip(data, 'utf8', (err, results) => {
89                        if (err) {
90                            rej(err);
91                        } else {
92                            res(results.toString('utf8'));
93                        }
94                    });
95                }
96            });
97        });
98    }
99

First check if the file we're looking to create exists, and if not, create one.

101    async create(sourceFileContents) {
102        const frameHash = hashFile(sourceFileContents);
103        const sourceFilePath = this.getHashedFilePath(sourceFileContents);
104        const exists = await this.has(sourceFilePath);
105        if (!exists) {
106            return new Promise((res, rej) => {

Before saving the file, gzip the text file

108                zlib.gzip(sourceFileContents, 'utf8', (err, results) => {
109                    if (err) {
110                        rej(err);
111                    } else {
112                        fs.writeFile(sourceFilePath, results, err => {
113                            if (err) {
114                                rej(err)
115                            } else {
116                                res(frameHash);
117                            }
118                        });
119                    }
120                });
121            });
122        }
123        return frameHash;
124    }
125
126}
127

Create a new database from the class, and export that for use.

129const store = new SourceFileStore(config.DATABASE);
130
131module.exports = {
132    store,
133}
134