Email Sorter Beta

Ich habe soweit automatisiert the Emails sortieren aber ich muss noch schauen was es fur bugs es gibt wenn die app online  ist deswegen wurde ich mit diesen Commit die website veroffentlichen obwohjl es sein konnte  das es noch nicht fertig ist und verkaufs bereit
This commit is contained in:
2026-01-22 19:32:12 +01:00
parent 95349af50b
commit abf761db07
596 changed files with 56405 additions and 51231 deletions

174
server/routes/api.mjs Normal file
View File

@@ -0,0 +1,174 @@
/**
* Main API Routes
* General API endpoints
*/
import express from 'express'
import { asyncHandler, NotFoundError, ValidationError } from '../middleware/errorHandler.mjs'
import { validate, schemas, rules } from '../middleware/validate.mjs'
import { respond } from '../utils/response.mjs'
import { products, questions, submissions, orders } from '../services/database.mjs'
import Stripe from 'stripe'
import { config } from '../config/index.mjs'
const router = express.Router()
const stripe = new Stripe(config.stripe.secretKey)
/**
* GET /api/products
* Get all active products
*/
router.get('/products', asyncHandler(async (req, res) => {
const productList = await products.getActive()
respond.success(res, productList)
}))
/**
* GET /api/questions
* Get questions for a product
*/
router.get('/questions', asyncHandler(async (req, res) => {
const { productSlug } = req.query
if (!productSlug) {
throw new ValidationError('productSlug ist erforderlich', { productSlug: ['Pflichtfeld'] })
}
const product = await products.getBySlug(productSlug)
if (!product) {
throw new NotFoundError('Produkt')
}
const questionList = await questions.getByProduct(product.$id)
respond.success(res, questionList)
}))
/**
* POST /api/submissions
* Create a new submission
*/
router.post('/submissions',
validate({
body: {
productSlug: [rules.required('productSlug')],
answers: [rules.required('answers'), rules.isObject('answers')],
},
}),
asyncHandler(async (req, res) => {
const { productSlug, answers } = req.body
const product = await products.getBySlug(productSlug)
if (!product) {
throw new NotFoundError('Produkt')
}
// Create submission
const submission = await submissions.create({
productId: product.$id,
status: 'draft',
customerEmail: answers.email || answers.customer_email || null,
customerName: answers.name || answers.customer_name || null,
finalSummaryJson: JSON.stringify(answers),
priceCents: product.priceCents,
currency: product.currency,
})
// Store answers separately
await orders.create(submission.$id, { answers })
respond.created(res, { submissionId: submission.$id })
})
)
/**
* POST /api/checkout
* Create Stripe checkout session for one-time payment
*/
router.post('/checkout',
validate({
body: {
submissionId: [rules.required('submissionId')],
},
}),
asyncHandler(async (req, res) => {
const { submissionId } = req.body
// Get submission
const submission = await submissions.create
let submissionDoc
try {
const { db, Collections } = await import('../services/database.mjs')
submissionDoc = await db.get(Collections.SUBMISSIONS, submissionId)
} catch (error) {
throw new NotFoundError('Submission')
}
// Create Stripe checkout session
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: [
{
price_data: {
currency: submissionDoc.currency || 'eur',
product_data: {
name: 'Email Sortierer Service',
description: 'Personalisiertes E-Mail-Sortier-Setup',
},
unit_amount: submissionDoc.priceCents || 4900,
},
quantity: 1,
},
],
mode: 'payment',
success_url: `${config.frontendUrl}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${config.frontendUrl}/cancel`,
metadata: {
submissionId,
},
customer_email: submissionDoc.customerEmail || undefined,
})
respond.success(res, { url: session.url, sessionId: session.id })
})
)
/**
* GET /api/submission/:id
* Get submission details
*/
router.get('/submission/:id', asyncHandler(async (req, res) => {
const { id } = req.params
const { db, Collections } = await import('../services/database.mjs')
const submission = await db.get(Collections.SUBMISSIONS, id)
// Don't expose sensitive data
respond.success(res, {
id: submission.$id,
status: submission.status,
createdAt: submission.$createdAt,
})
}))
/**
* GET /api/config
* Get public configuration
*/
router.get('/config', (req, res) => {
respond.success(res, {
features: {
gmail: Boolean(config.google.clientId),
outlook: Boolean(config.microsoft.clientId),
ai: Boolean(config.mistral.apiKey),
},
pricing: {
basic: { price: 9, currency: 'EUR', accounts: 1 },
pro: { price: 19, currency: 'EUR', accounts: 3 },
business: { price: 49, currency: 'EUR', accounts: 10 },
},
})
})
export default router