This repository was archived by the owner on Dec 20, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 13
Convert to SQLite+S3 backend rather than a dedicated PostgreSQL database #33
Open
colebrumley
wants to merge
8
commits into
master
Choose a base branch
from
colebrumley/sqlite-s3
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 5 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
b95f4f4
Refactored to remove PG dependency in favor of a local sqlite db
colebrumley f3cbded
Add the ability to skip S3
colebrumley 5a4cb97
Update Readme
colebrumley d209ffc
Remove commented code
colebrumley a940de7
Update readme with new info
colebrumley 9c93790
Switch to better-sqlite3 per Tim's advice
colebrumley 23420bd
Adding a basic dockerfile for the backend
colebrumley 3696028
Fix port
colebrumley File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,3 +21,5 @@ node_modules | |
| npm-debug.log* | ||
| yarn-debug.log* | ||
| yarn-error.log* | ||
|
|
||
| chromium_downloads.db | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,47 +1,24 @@ | ||
| const Sequelize = require("sequelize"); | ||
| const sqlite3 = require('sqlite3').verbose(); | ||
| const db = new sqlite3.Database('./chromium_downloads.db'); | ||
|
|
||
| let DATABASE_URL = process.env.DATABASE_URL; | ||
| // Define the schema and create tables if they don't exist | ||
| db.serialize(() => { | ||
| db.run(`CREATE TABLE IF NOT EXISTS builds ( | ||
| id INTEGER PRIMARY KEY AUTOINCREMENT, | ||
| version TEXT, | ||
| os TEXT, | ||
| channel TEXT, | ||
| timestamp TEXT, | ||
| baseRevision TEXT, | ||
| artifactsRevision TEXT, | ||
| downloads TEXT | ||
| )`, (err) => { | ||
| if (err) { | ||
| console.error('Error creating builds table', err); | ||
| } else { | ||
| console.log('Successfully ensured the builds table exists'); | ||
| } | ||
| }); | ||
| }); | ||
|
|
||
| if (process.env.NODE_ENV === "production") { | ||
| DATABASE_URL += "?ssl=true"; | ||
| } | ||
|
|
||
| const sequelize = new Sequelize(DATABASE_URL); | ||
|
|
||
| class Build extends Sequelize.Model {} | ||
| Build.init( | ||
| { | ||
| version: Sequelize.STRING, | ||
| os: Sequelize.STRING, | ||
| channel: Sequelize.STRING, | ||
| timestamp: Sequelize.DATE, | ||
| baseRevision: Sequelize.STRING, | ||
| artifactsRevision: Sequelize.STRING, | ||
| downloads: Sequelize.JSONB, | ||
| }, | ||
| { | ||
| sequelize, | ||
| modelName: "builds", | ||
| timestamps: false, | ||
| indexes: [ | ||
| { | ||
| unique: true, | ||
| fields: ["version", "os", "channel", "timestamp"], | ||
| }, | ||
| ], | ||
| pool: { | ||
| max: 4, | ||
| min: 1, | ||
| }, | ||
| } | ||
| ); | ||
|
|
||
| async function initialize() { | ||
| console.log(sequelize); | ||
| sequelize.sync(); | ||
| } | ||
|
|
||
| module.exports = { | ||
| initialize, | ||
| Build, | ||
| }; | ||
| module.exports = db; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,55 +1,77 @@ | ||
| const db = require("./db"); | ||
| const express = require("express"); | ||
| const db = require("./db"); | ||
| const scraper = require("./scraper"); | ||
| const { downloadDbFromS3, uploadDbToS3 } = require('./s3'); | ||
|
|
||
| const PORT = Number(process.env.PORT) || 3001; | ||
|
|
||
| const app = express(); | ||
|
|
||
| // Middleware to allow cross-origin requests | ||
| app.use((req, res, next) => { | ||
| res.setHeader("access-control-allow-origin", "*"); | ||
|
|
||
| next(); | ||
| }); | ||
|
|
||
| app.get("/builds", (req, res) => { | ||
| db.Build.findAll({ | ||
| attributes: ["version", "os", "channel", "timestamp"], | ||
| order: [["timestamp", "DESC"]], | ||
| }).then((builds) => { | ||
| // Route definitions | ||
| // Assuming `db` is the sqlite3 database object and is properly initialized | ||
|
|
||
| app.get("/builds", async (req, res) => { | ||
| const query = "SELECT version, os, channel, timestamp FROM builds ORDER BY timestamp DESC"; | ||
| db.all(query, [], (error, builds) => { | ||
| if (error) { | ||
| console.error(error); | ||
| return res.sendStatus(500); | ||
| } | ||
| res.json(builds); | ||
| }); | ||
| }); | ||
|
|
||
| app.get("/builds/:version/:channel/:os", (req, res) => { | ||
| db.Build.findAll({ | ||
| where: { | ||
| channel: req.params.channel, | ||
| os: req.params.os, | ||
| version: req.params.version, | ||
| }, | ||
| }).then((builds) => { | ||
| if (!builds.length) { | ||
| app.get("/builds/:version/:channel/:os", async (req, res) => { | ||
| const { version, channel, os } = req.params; | ||
| const query = "SELECT * FROM builds WHERE channel = ? AND os = ? AND version = ?"; | ||
| db.all(query, [channel, os, version], (error, builds) => { | ||
| if (error) { | ||
| console.error(error); | ||
| return res.sendStatus(500); | ||
| } | ||
| if (builds.length === 0) { | ||
| return res.sendStatus(404); | ||
| } | ||
|
|
||
| res.json(builds[0]); | ||
| }); | ||
| }); | ||
|
|
||
| console.log("Initializing"); | ||
|
|
||
| console.log(db.initialize); | ||
|
|
||
| db.initialize() | ||
| .then(() => { | ||
| console.log("Starting scraping"); | ||
| // Server startup logic | ||
| async function startServer() { | ||
| try { | ||
| await downloadDbFromS3(); | ||
| console.log("Database downloaded from S3"); | ||
| scraper.start(); | ||
|
|
||
| console.log("Scraper started"); | ||
| app.listen(PORT, () => { | ||
| console.log(`Backend listening on ${PORT}.`); | ||
| }); | ||
| }) | ||
| .catch((e) => { | ||
| console.error(e); | ||
| }); | ||
| } catch (error) { | ||
| console.error(error); | ||
| process.exit(1); | ||
| } | ||
| } | ||
|
|
||
| // Shutdown logic | ||
| async function handleShutdown() { | ||
| try { | ||
| await uploadDbToS3(); | ||
| console.log("Database uploaded to S3"); | ||
| process.exit(0); | ||
| } catch (error) { | ||
| console.error(error); | ||
| process.exit(1); | ||
| } | ||
| } | ||
|
|
||
| // Handle shutdown signals | ||
| process.on('SIGINT', handleShutdown); | ||
| process.on('SIGTERM', handleShutdown); | ||
|
|
||
| // Initialize the server | ||
| startServer(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| // backend/s3.js | ||
| const { S3Client, GetObjectCommand } = require('@aws-sdk/client-s3'); | ||
| const { Upload } = require('@aws-sdk/lib-storage'); | ||
| const fs = require('fs'); | ||
| const sqlite3 = require('sqlite3').verbose(); | ||
|
|
||
| const s3Client = new S3Client({ | ||
| region: process.env.AWS_REGION || 'us-east-1' | ||
| }); | ||
|
|
||
| const BUCKET_NAME = process.env.S3_BUCKET_NAME; | ||
| const DB_FILE_NAME = 'chromium_downloads.db'; | ||
| const SKIP_S3_INTEGRATION = process.env.SKIP_S3_INTEGRATION === 'true'; | ||
|
|
||
| async function uploadDbToS3() { | ||
| if (SKIP_S3_INTEGRATION) { | ||
| return; | ||
| } | ||
|
|
||
| const fileStream = fs.createReadStream(DB_FILE_NAME); | ||
| const uploadParams = { | ||
| Bucket: BUCKET_NAME, | ||
| Key: DB_FILE_NAME, | ||
| Body: fileStream | ||
| }; | ||
| const upload = new Upload({ | ||
| client: s3Client, | ||
| params: uploadParams | ||
| }); | ||
|
|
||
| await upload.done(); | ||
| } | ||
|
|
||
| async function downloadDbFromS3() { | ||
| if (SKIP_S3_INTEGRATION) { | ||
| return; | ||
| } | ||
|
|
||
| const getObjectParams = { | ||
| Bucket: BUCKET_NAME, | ||
| Key: DB_FILE_NAME | ||
| }; | ||
| try { | ||
| const { Body } = await s3Client.send(new GetObjectCommand(getObjectParams)); | ||
| const fileStream = Body.pipe(fs.createWriteStream(DB_FILE_NAME)); | ||
| await new Promise((resolve, reject) => { | ||
| fileStream.on('error', reject); | ||
| fileStream.on('close', resolve); | ||
| }); | ||
| } catch (err) { | ||
| if (err.name === 'NoSuchKey') { | ||
| // Create an empty file | ||
| fs.writeFileSync(DB_FILE_NAME, ''); | ||
| // Initialize the database and create the builds table | ||
| const db = new sqlite3.Database(DB_FILE_NAME); | ||
| db.serialize(() => { | ||
| db.run(`CREATE TABLE IF NOT EXISTS builds ( | ||
| id INTEGER PRIMARY KEY AUTOINCREMENT, | ||
| version TEXT, | ||
| os TEXT, | ||
| channel TEXT, | ||
| timestamp TEXT, | ||
| baseRevision TEXT, | ||
| artifactsRevision TEXT, | ||
| downloads TEXT | ||
| )`, (err) => { | ||
| if (err) { | ||
| console.error('Error creating builds table', err); | ||
| } else { | ||
| console.log('Successfully created the builds table in new DB file'); | ||
| } | ||
| }); | ||
| }); | ||
| db.close(); | ||
| } else { | ||
| throw err; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| module.exports = { uploadDbToS3, downloadDbFromS3 }; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.