Initial setup for ESV Bible Markdown project
This commit is contained in:
67
.gitignore
vendored
Normal file
67
.gitignore
vendored
Normal 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
18
Dockerfile
Normal 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
45
README.md
Normal 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
12
docker-compose.yml
Normal 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
21
package.json
Normal 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
115
src/index.js
Normal 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}`);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user