feat: initialisation projet Hono.js + TypeScript + Vitest

This commit is contained in:
Hermann_Kitio 2026-04-16 06:37:25 +03:00
parent b06970c9ae
commit 708517edef
13 changed files with 3125 additions and 131 deletions

19
.env.example Normal file
View file

@ -0,0 +1,19 @@
# Supabase
SUPABASE_URL=https://xxx.supabase.co
SUPABASE_SERVICE_ROLE_KEY=xxx
# APIs
DEEPSEEK_API_KEY=xxx
GEMINI_API_KEY=xxx
# Stripe
STRIPE_SECRET_KEY=xxx
STRIPE_WEBHOOK_SECRET=xxx
STRIPE_PRICE_STANDARD=price_xxx
STRIPE_PRICE_PREMIUM=price_xxx
# Config
PORT=3000
APP_URL=https://expria.app
API_URL=https://api.expria.app
NODE_ENV=production

134
.gitignore vendored
View file

@ -1,132 +1,4 @@
# ---> Node node_modules
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist dist
.env
# Gatsby files .env.local
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

2957
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

26
package.json Normal file
View file

@ -0,0 +1,26 @@
{
"name": "expria-backend",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage"
},
"dependencies": {
"@hono/node-server": "^1.13.7",
"@supabase/supabase-js": "^2.49.4",
"hono": "^4.7.7",
"stripe": "^17.7.0"
},
"devDependencies": {
"@types/node": "^22.15.3",
"@vitest/coverage-v8": "^3.1.2",
"tsx": "^4.19.3",
"typescript": "^5.8.3",
"vitest": "^3.1.2"
}
}

0
src/controllers/.gitkeep Normal file
View file

14
src/index.ts Normal file
View file

@ -0,0 +1,14 @@
import { Hono } from 'hono'
import { serve } from '@hono/node-server'
const app = new Hono()
app.get('/', (c) => {
return c.json({ message: 'Expria API — OK' }, 200)
})
const port = Number(process.env.PORT) || 3000
serve({ fetch: app.fetch, port }, () => {
console.log(`Expria API listening on port ${port}`)
})

View file

74
src/lib/access.ts Normal file
View file

@ -0,0 +1,74 @@
export type Plan = 'free' | 'standard' | 'premium'
export type Feature =
| 'oral_t2_live'
| 'detailed_report'
| 'tips'
| 'dashboard'
| 'exam_mode'
| 'pattern_analysis'
| 'preparation_index'
| 'basic_report'
export const PLANS = {
free: {
simulations_lifetime: 5,
oral_t2_live: false,
detailed_report: false,
tips: false,
dashboard: false,
exam_mode: false,
pattern_analysis: false,
preparation_index: false,
},
standard: {
simulations_lifetime: null,
oral_t2_live: false,
detailed_report: true,
tips: true,
dashboard: true,
exam_mode: false,
pattern_analysis: false,
preparation_index: false,
},
premium: {
simulations_lifetime: null,
oral_t2_live: true,
detailed_report: true,
tips: true,
dashboard: true,
exam_mode: true,
pattern_analysis: true,
preparation_index: true,
},
}
export function getPlanPermissions(plan: Plan) {
const perms = PLANS[plan]
if (!perms) {
throw new Error(`Plan inconnu : ${plan}`)
}
return perms
}
export function canUserSimulate(user: { plan: string; simulations_used: number }): {
allowed: boolean
reason?: string
} {
if (!(user.plan in PLANS)) {
return { allowed: false, reason: 'invalid_plan' }
}
const plan = user.plan as Plan
const perms = PLANS[plan]
if (perms.simulations_lifetime !== null && user.simulations_used >= perms.simulations_lifetime) {
return { allowed: false, reason: 'quota_reached' }
}
return { allowed: true }
}
export function checkFeatureAccess(plan: Plan, feature: Feature): boolean {
if (feature === 'basic_report') return true
const perms = PLANS[plan]
if (!perms) return false
return perms[feature as keyof typeof perms] === true
}

0
src/middleware/.gitkeep Normal file
View file

0
src/routes/.gitkeep Normal file
View file

0
src/types/.gitkeep Normal file
View file

20
tsconfig.json Normal file
View file

@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"lib": ["ES2022"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

12
vitest.config.ts Normal file
View file

@ -0,0 +1,12 @@
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
environment: 'node',
globals: true,
coverage: {
reporter: ['text', 'html'],
include: ['src/lib/**', 'src/controllers/**'],
},
},
})