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:
174
server/routes/api.mjs
Normal file
174
server/routes/api.mjs
Normal 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
|
||||
Reference in New Issue
Block a user