Fix authentication state persistence and admin role display
- Implement complete authentication system with JWT token validation - Add auth provider with persistent login state across page refreshes - Create multilingual login/register forms with Material-UI components - Fix token validation using raw SQL queries to bypass Prisma sync issues - Add comprehensive error handling for expired/invalid tokens - Create profile and settings pages with full i18n support - Add proper user role management (admin/user) with database sync - Implement secure middleware with CSRF protection and auth checks - Add debug endpoints for troubleshooting authentication issues - Fix Zustand store persistence for authentication state 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
145
scripts/validate-bsb-md.ts
Normal file
145
scripts/validate-bsb-md.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
#!/usr/bin/env tsx
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
const SRC = process.env.BSB_MD_PATH || path.join('bibles', 'bible-bsb.md')
|
||||
|
||||
type BookInfo = { name: string; variants: string[]; expectedChapters: number; testament: 'OT'|'NT' }
|
||||
|
||||
function canon(): BookInfo[] {
|
||||
const OT: Array<[string, string[], number]> = [
|
||||
['Genesis', ['Genesis'], 50],
|
||||
['Exodus', ['Exodus'], 40],
|
||||
['Leviticus', ['Leviticus'], 27],
|
||||
['Numbers', ['Numbers'], 36],
|
||||
['Deuteronomy', ['Deuteronomy'], 34],
|
||||
['Joshua', ['Joshua'], 24],
|
||||
['Judges', ['Judges'], 21],
|
||||
['Ruth', ['Ruth'], 4],
|
||||
['1 Samuel', ['1 Samuel','1 Samuel'], 31],
|
||||
['2 Samuel', ['2 Samuel','2 Samuel'], 24],
|
||||
['1 Kings', ['1 Kings','1 Kings'], 22],
|
||||
['2 Kings', ['2 Kings','2 Kings'], 25],
|
||||
['1 Chronicles', ['1 Chronicles','1 Chronicles'], 29],
|
||||
['2 Chronicles', ['2 Chronicles','2 Chronicles','2 Chronicles'], 36],
|
||||
['Ezra', ['Ezra'], 10],
|
||||
['Nehemiah', ['Nehemiah'], 13],
|
||||
['Esther', ['Esther'], 10],
|
||||
['Job', ['Job'], 42],
|
||||
['Psalms', ['Psalms','Psalm'], 150],
|
||||
['Proverbs', ['Proverbs'], 31],
|
||||
['Ecclesiastes', ['Ecclesiastes'], 12],
|
||||
['Song of Songs', ['Song of Songs','Song of Solomon'], 8],
|
||||
['Isaiah', ['Isaiah'], 66],
|
||||
['Jeremiah', ['Jeremiah'], 52],
|
||||
['Lamentations', ['Lamentations'], 5],
|
||||
['Ezekiel', ['Ezekiel'], 48],
|
||||
['Daniel', ['Daniel'], 12],
|
||||
['Hosea', ['Hosea'], 14],
|
||||
['Joel', ['Joel'], 3],
|
||||
['Amos', ['Amos'], 9],
|
||||
['Obadiah', ['Obadiah'], 1],
|
||||
['Jonah', ['Jonah'], 4],
|
||||
['Micah', ['Micah'], 7],
|
||||
['Nahum', ['Nahum'], 3],
|
||||
['Habakkuk', ['Habakkuk'], 3],
|
||||
['Zephaniah', ['Zephaniah'], 3],
|
||||
['Haggai', ['Haggai'], 2],
|
||||
['Zechariah', ['Zechariah'], 14],
|
||||
['Malachi', ['Malachi'], 4]
|
||||
]
|
||||
const NT: Array<[string, string[], number]> = [
|
||||
['Matthew', ['Matthew'], 28],
|
||||
['Mark', ['Mark'], 16],
|
||||
['Luke', ['Luke'], 24],
|
||||
['John', ['John'], 21],
|
||||
['Acts', ['Acts'], 28],
|
||||
['Romans', ['Romans'], 16],
|
||||
['1 Corinthians', ['1 Corinthians','1 Corinthians'], 16],
|
||||
['2 Corinthians', ['2 Corinthians','2 Corinthians'], 13],
|
||||
['Galatians', ['Galatians'], 6],
|
||||
['Ephesians', ['Ephesians'], 6],
|
||||
['Philippians', ['Philippians'], 4],
|
||||
['Colossians', ['Colossians'], 4],
|
||||
['1 Thessalonians', ['1 Thessalonians','1 Thessalonians'], 5],
|
||||
['2 Thessalonians', ['2 Thessalonians','2 Thessalonians'], 3],
|
||||
['1 Timothy', ['1 Timothy','1 Timothy'], 6],
|
||||
['2 Timothy', ['2 Timothy','2 Timothy'], 4],
|
||||
['Titus', ['Titus'], 3],
|
||||
['Philemon', ['Philemon'], 1],
|
||||
['Hebrews', ['Hebrews'], 13],
|
||||
['James', ['James'], 5],
|
||||
['1 Peter', ['1 Peter','1 Peter'], 5],
|
||||
['2 Peter', ['2 Peter','2 Peter'], 3],
|
||||
['1 John', ['1 John','1 John'], 5],
|
||||
['2 John', ['2 John','2 John'], 1],
|
||||
['3 John', ['3 John','3 John'], 1],
|
||||
['Jude', ['Jude'], 1],
|
||||
['Revelation', ['Revelation'], 22]
|
||||
]
|
||||
return [
|
||||
...OT.map(([n,v,c]) => ({ name:n, variants:v, expectedChapters:c, testament:'OT' as const })),
|
||||
...NT.map(([n,v,c]) => ({ name:n, variants:v, expectedChapters:c, testament:'NT' as const })),
|
||||
]
|
||||
}
|
||||
|
||||
function escapeRegExp(s: string) {
|
||||
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||||
}
|
||||
|
||||
function main() {
|
||||
if (!fs.existsSync(SRC)) {
|
||||
console.error('Missing source file:', SRC)
|
||||
process.exit(1)
|
||||
}
|
||||
const md = fs.readFileSync(SRC, 'utf-8')
|
||||
const books = canon()
|
||||
|
||||
const report: any = { file: SRC, totals: { versesTagged: 0 }, books: [] as any[] }
|
||||
|
||||
for (const b of books) {
|
||||
// Build a regex to find markers like: "... | BookName 12:34" or just "BookName 12:34"
|
||||
// Allow flexible whitespace and the double-spaced variants in source.
|
||||
const patterns = b.variants.map(v => v.replace(/\s+/g, '\\s+'))
|
||||
const combined = patterns.map(p => `(?:^|[\n\r\f\s\|])${p}\\s+(\\d+):(\\d+)`).join('|')
|
||||
const re = new RegExp(combined, 'gi')
|
||||
const chapters = new Set<number>()
|
||||
let m: RegExpExecArray | null
|
||||
let verseCount = 0
|
||||
while ((m = re.exec(md)) !== null) {
|
||||
// Find first numeric capture among alternations
|
||||
const nums = m.slice(1).filter(Boolean)
|
||||
const ch = parseInt(nums[0] || '0', 10)
|
||||
const vs = parseInt(nums[1] || '0', 10)
|
||||
if (Number.isFinite(ch) && ch > 0) chapters.add(ch)
|
||||
if (Number.isFinite(vs) && vs > 0) verseCount++
|
||||
}
|
||||
|
||||
report.totals.versesTagged += verseCount
|
||||
report.books.push({
|
||||
name: b.name,
|
||||
testament: b.testament,
|
||||
expectedChapters: b.expectedChapters,
|
||||
detectedChapters: [...chapters].sort((a,b)=>a-b),
|
||||
detectedCount: chapters.size,
|
||||
coverage: b.expectedChapters > 0 ? +(100 * chapters.size / b.expectedChapters).toFixed(2) : null,
|
||||
verseMarkers: verseCount
|
||||
})
|
||||
}
|
||||
|
||||
const missingBooks = report.books.filter((x:any) => x.detectedCount === 0).map((x:any)=>x.name)
|
||||
const partialBooks = report.books.filter((x:any) => x.detectedCount > 0 && x.detectedCount < x.expectedChapters).map((x:any)=>({name:x.name, det:x.detectedCount, exp:x.expectedChapters}))
|
||||
|
||||
console.log('Validation summary for', SRC)
|
||||
console.log('Total verse markers found:', report.totals.versesTagged)
|
||||
console.log('Books missing markers:', missingBooks.length ? missingBooks.join(', ') : 'None')
|
||||
console.log('Books partially detected (chapters):', partialBooks.length ? partialBooks.slice(0,10) : 'None')
|
||||
|
||||
const outDir = path.join('data','en_bible','BSB_VALIDATION')
|
||||
fs.mkdirSync(outDir, { recursive: true })
|
||||
fs.writeFileSync(path.join(outDir,'report.json'), JSON.stringify(report, null, 2), 'utf-8')
|
||||
console.log('Wrote detailed report to', path.join(outDir,'report.json'))
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user