Initial setup for ESV Bible Markdown project

This commit is contained in:
Ryderjj89
2025-09-13 11:58:52 -04:00
commit 921a233c51
6 changed files with 278 additions and 0 deletions

67
.gitignore vendored Normal file
View File

@@ -0,0 +1,67 @@
# Dependencies
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Coverage directory used by tools like istanbul
coverage/
# nyc test coverage
.nyc_output
# Dependency directories
jspm_packages/
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# IDE files
.vscode/
.idea/
*.swp
*.swo
# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Docker
.dockerignore
# Bible data (if not committed)
bible-data/

18
Dockerfile Normal file
View File

@@ -0,0 +1,18 @@
FROM node:18-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy application code
COPY . .
# Expose port
EXPOSE 3000
# Start the application
CMD ["npm", "start"]

45
README.md Normal file
View File

@@ -0,0 +1,45 @@
# ESV Bible Markdown
A Docker-based web service for serving the ESV Bible in Markdown format.
## Features
- Complete ESV Bible text in Markdown format
- Docker containerized for easy deployment
- RESTful API for accessing Bible content
- Optimized for remote hosting
## Setup
1. Clone this repository
2. Place ESV Bible Markdown files in the `bible-data` directory
3. Run `docker-compose up --build`
## Usage
The service will be available at `http://localhost:3000`
### API Endpoints
- `GET /books` - List all books
- `GET /books/:book` - Get specific book
- `GET /books/:book/:chapter` - Get specific chapter
## Development
For local development:
```bash
npm install
npm run dev
```
## Docker Deployment
Build and run with Docker Compose:
```bash
docker-compose up --build
```
## License
MIT

12
docker-compose.yml Normal file
View File

@@ -0,0 +1,12 @@
version: '3.8'
services:
esv-bible:
build: .
ports:
- "3000:3000"
volumes:
- ./bible-data:/app/bible-data:ro
environment:
- NODE_ENV=production
restart: unless-stopped

21
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"
}
}

115
src/index.js Normal file
View File

@@ -0,0 +1,115 @@
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 files = await fs.readdir(BIBLE_DATA_DIR);
const markdownFiles = files.filter(file => file.endsWith('.md'));
return markdownFiles.map(file => file.replace('.md', ''));
} 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 filePath = path.join(BIBLE_DATA_DIR, `${book}.md`);
const content = await readMarkdownFile(filePath);
res.type('text/markdown').send(content);
} 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 filePath = path.join(BIBLE_DATA_DIR, `${book}.md`);
const content = await readMarkdownFile(filePath);
// Simple chapter extraction (this could be improved with better parsing)
const lines = content.split('\n');
const chapterPattern = new RegExp(`^# ${chapter}\\s*$`, 'i');
const chapterLines = [];
let inChapter = false;
for (const line of lines) {
if (chapterPattern.test(line)) {
inChapter = true;
chapterLines.push(line);
} else if (inChapter && line.startsWith('# ')) {
// Next chapter starts
break;
} else if (inChapter) {
chapterLines.push(line);
}
}
if (chapterLines.length === 0) {
return res.status(404).json({ error: `Chapter ${chapter} not found in ${book}` });
}
res.type('text/markdown').send(chapterLines.join('\n'));
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// 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}`);
});