Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added arm support #12

Merged
merged 3 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,18 @@ jobs:
id: docker_tag
run: echo "TAG=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:pr-${{ github.event.pull_request.number }}" >> $GITHUB_ENV

- name: Set up QEMU for cross-platform builds
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
install: true

- name: Build and Push Docker image for Testing
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ env.TAG }}
platforms: linux/amd64,linux/arm64,linux/arm/v7
tags: ${{ env.TAG }}
9 changes: 9 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,20 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set up QEMU for cross-platform builds
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
install: true

- name: Build and Push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
platforms: linux/amd64,linux/arm64,linux/arm/v7
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event.release.tag_name }}
16 changes: 13 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@
"license": "ISC",
"description": "",
"dependencies": {
"better-sqlite3": "^11.5.0",
"body-parser": "^1.20.3",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.21.1",
"openai": "^4.67.3",
"sqlite": "^5.1.1",
"sqlite3": "^5.1.7"
"openai": "^4.67.3"
},
"devDependencies": {
"jest": "^29.7.0",
Expand Down
23 changes: 11 additions & 12 deletions server/src/config/database.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const sqlite3 = require('sqlite3');
const { open } = require('sqlite');
const Database = require('better-sqlite3');
const path = require('path');
const fs = require('fs');

Expand All @@ -23,21 +22,23 @@ function getDatabasePath() {
}
}

async function initializeDatabase() {
function initializeDatabase() {
try {
const dbPath = getDatabasePath();
console.log(`Initializing SQLite database at: ${dbPath}`);

db = await open({
filename: dbPath,
driver: sqlite3.Database
// Open database with WAL mode for better performance
db = new Database(dbPath, {
verbose: console.log,
fileMustExist: false
});

// Enable foreign keys
await db.run('PRAGMA foreign_keys = ON');
// Enable foreign keys and WAL mode
db.pragma('foreign_keys = ON');
db.pragma('journal_mode = WAL');

// Create snippets table with timestamp
await db.exec(`
db.exec(`
CREATE TABLE IF NOT EXISTS snippets (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
Expand All @@ -61,12 +62,11 @@ async function createBackup() {
const dbPath = getDatabasePath();
const backupPath = getBackupPath();

// Check if source database exists
if (!fs.existsSync(dbPath)) {
throw new Error('Source database does not exist');
}

// Create backup using stream to handle large files
// Backup using stream
await new Promise((resolve, reject) => {
const readStream = fs.createReadStream(dbPath);
const writeStream = fs.createWriteStream(backupPath);
Expand All @@ -78,7 +78,6 @@ async function createBackup() {
readStream.pipe(writeStream);
});

// Verify backup file exists and has content
const backupStats = fs.statSync(backupPath);
if (backupStats.size === 0) {
throw new Error('Backup file was created but is empty');
Expand Down
152 changes: 85 additions & 67 deletions server/src/repositories/snippetRepository.js
Original file line number Diff line number Diff line change
@@ -1,100 +1,118 @@
const { getDb } = require('../config/database');

class SnippetRepository {
// Helper method to format SELECT statements with proper UTC handling
#getSelectQuery(additional = '') {
return `
SELECT
id,
title,
language,
description,
code,
datetime(updated_at) || 'Z' as updated_at
FROM snippets
${additional}
`.trim();
constructor() {
this.selectAllStmt = null;
this.insertStmt = null;
this.updateStmt = null;
this.deleteStmt = null;
this.selectByIdStmt = null;
}

async findAll() {
#initializeStatements() {
const db = getDb();

if (!this.selectAllStmt) {
this.selectAllStmt = db.prepare(`
SELECT
id,
title,
language,
description,
code,
datetime(updated_at) || 'Z' as updated_at
FROM snippets
ORDER BY updated_at DESC
`);

this.insertStmt = db.prepare(`
INSERT INTO snippets (
title,
language,
description,
code,
updated_at
) VALUES (?, ?, ?, ?, datetime('now', 'utc'))
`);

this.updateStmt = db.prepare(`
UPDATE snippets
SET title = ?,
language = ?,
description = ?,
code = ?,
updated_at = datetime('now', 'utc')
WHERE id = ?
`);

this.deleteStmt = db.prepare('DELETE FROM snippets WHERE id = ?');

this.selectByIdStmt = db.prepare(`
SELECT
id,
title,
language,
description,
code,
datetime(updated_at) || 'Z' as updated_at
FROM snippets
WHERE id = ?
`);
}
}

findAll() {
this.#initializeStatements();
try {
const snippets = await db.all(
this.#getSelectQuery('ORDER BY updated_at DESC')
);
return snippets;
return this.selectAllStmt.all();
} catch (error) {
console.error('Error in findAll:', error);
throw error;
}
}

async create({ title, language, description, code }) {
const db = getDb();
create({ title, language, description, code }) {
this.#initializeStatements();
try {
const result = await db.run(
`INSERT INTO snippets (
title,
language,
description,
code,
updated_at
) VALUES (?, ?, ?, ?, datetime('now', 'utc'))`,
[title, language, description, code]
);
const db = getDb();
const result = db.transaction(() => {
const insertResult = this.insertStmt.run(title, language, description, code);
return this.selectByIdStmt.get(insertResult.lastInsertRowid);
})();

// Fetch the created snippet with UTC formatting
const created = await db.get(
this.#getSelectQuery('WHERE id = ?'),
[result.lastID]
);
return created;
return result;
} catch (error) {
console.error('Error in create:', error);
console.error('Parameters:', { title, language, description, code });
throw error;
}
}

async delete(id) {
const db = getDb();
delete(id) {
this.#initializeStatements();
try {
// Only fetch necessary fields for deletion confirmation
const snippet = await db.get(
this.#getSelectQuery('WHERE id = ?'),
[id]
);

if (snippet) {
await db.run('DELETE FROM snippets WHERE id = ?', [id]);
}

return snippet;
const db = getDb();
return db.transaction(() => {
const snippet = this.selectByIdStmt.get(id);
if (snippet) {
this.deleteStmt.run(id);
}
return snippet;
})();
} catch (error) {
console.error('Error in delete:', error);
throw error;
}
}

async update(id, { title, language, description, code }) {
const db = getDb();
update(id, { title, language, description, code }) {
this.#initializeStatements();
try {
await db.run(
`UPDATE snippets
SET title = ?,
language = ?,
description = ?,
code = ?,
updated_at = datetime('now', 'utc')
WHERE id = ?`,
[title, language, description, code, id]
);

// Return updated snippet with UTC formatting
return db.get(
this.#getSelectQuery('WHERE id = ?'),
[id]
);
const db = getDb();
return db.transaction(() => {
this.updateStmt.run(title, language, description, code, id);
return this.selectByIdStmt.get(id);
})();
} catch (error) {
console.error('Error in update:', error);
throw error;
Expand Down