Reorganize project structure with backend/ and frontend/ directories

This commit is contained in:
Ryderjj89
2025-09-13 12:14:01 -04:00
parent 84f1dfaf23
commit 13c93879c0
6 changed files with 130 additions and 26 deletions

27
backend/Dockerfile Normal file
View File

@@ -0,0 +1,27 @@
FROM node:18-alpine
WORKDIR /app
# Install git for cloning the bible repository
RUN apk add --no-cache git
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy application code
COPY . .
# Clone ESV Bible markdown repository
RUN git clone https://github.com/lguenth/mdbible.git /tmp/mdbible && \
mkdir -p /app/bible-data && \
cp -r /tmp/mdbible/by_chapter/* /app/bible-data/ && \
rm -rf /tmp/mdbible
# Expose port
EXPOSE 3000
# Start the application
CMD ["npm", "start"]

21
backend/package.json Normal file
View File

@@ -0,0 +1,21 @@
{
"name": "esv-bible-markdown",
"version": "1.0.0",
"description": "ESV Bible in Markdown format served via Docker",
"main": "src/index.js",
"scripts": {
"start": "node src/index.js",
"dev": "nodemon src/index.js"
},
"keywords": ["bible", "esv", "markdown", "docker"],
"author": "",
"license": "MIT",
"dependencies": {
"express": "^4.18.2",
"cors": "^2.8.5",
"helmet": "^7.1.0"
},
"devDependencies": {
"nodemon": "^3.0.1"
}
}

132
backend/src/index.js Normal file
View File

@@ -0,0 +1,132 @@
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const path = require('path');
const fs = require('fs').promises;
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(helmet());
app.use(cors());
app.use(express.json());
// Bible data directory
const BIBLE_DATA_DIR = path.join(__dirname, '../bible-data');
// Helper function to read markdown files
async function readMarkdownFile(filePath) {
try {
const content = await fs.readFile(filePath, 'utf-8');
return content;
} catch (error) {
throw new Error(`Failed to read file: ${filePath}`);
}
}
// Helper function to get all books
async function getBooks() {
try {
const items = await fs.readdir(BIBLE_DATA_DIR);
const bookDirs = [];
for (const item of items) {
const itemPath = path.join(BIBLE_DATA_DIR, item);
const stat = await fs.stat(itemPath);
if (stat.isDirectory()) {
// Check if directory contains markdown files
try {
const files = await fs.readdir(itemPath);
if (files.some(file => file.endsWith('.md'))) {
bookDirs.push(item);
}
} catch (error) {
// Skip directories we can't read
continue;
}
}
}
return bookDirs;
} catch (error) {
throw new Error('Failed to read bible data directory');
}
}
// Routes
app.get('/health', (req, res) => {
res.json({ status: 'OK', message: 'ESV Bible API is running' });
});
app.get('/books', async (req, res) => {
try {
const books = await getBooks();
res.json({ books });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.get('/books/:book', async (req, res) => {
try {
const { book } = req.params;
const bookDir = path.join(BIBLE_DATA_DIR, book);
// Check if book directory exists
const stat = await fs.stat(bookDir);
if (!stat.isDirectory()) {
return res.status(404).json({ error: `Book '${book}' not found` });
}
// Get all chapter files
const files = await fs.readdir(bookDir);
const chapterFiles = files.filter(file => file.endsWith('.md')).sort();
if (chapterFiles.length === 0) {
return res.status(404).json({ error: `No chapters found for book '${book}'` });
}
// Combine all chapters
let fullBook = `# ${book}\n\n`;
for (const chapterFile of chapterFiles) {
const chapterPath = path.join(bookDir, chapterFile);
const chapterContent = await readMarkdownFile(chapterPath);
fullBook += chapterContent + '\n\n';
}
res.type('text/markdown').send(fullBook);
} catch (error) {
res.status(404).json({ error: `Book '${req.params.book}' not found` });
}
});
app.get('/books/:book/:chapter', async (req, res) => {
try {
const { book, chapter } = req.params;
const chapterPath = path.join(BIBLE_DATA_DIR, book, `${chapter}.md`);
const content = await readMarkdownFile(chapterPath);
res.type('text/markdown').send(content);
} catch (error) {
res.status(404).json({ error: `Chapter ${chapter} not found in book '${book}'` });
}
});
// Error handling middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Something went wrong!' });
});
// 404 handler
app.use((req, res) => {
res.status(404).json({ error: 'Endpoint not found' });
});
// Start server
app.listen(PORT, () => {
console.log(`ESV Bible API server running on port ${PORT}`);
console.log(`Bible data directory: ${BIBLE_DATA_DIR}`);
});