fix(dev): Vite-API-Proxy, Auth, Stripe-Mails und Backend-Erweiterungen
- Client: API-Basis-URL (joinApiUrl, /v1-Falle), Vite strictPort + Proxy 127.0.0.1, Nicht-JSON-Fehler - Server: /api-404 ohne Wildcard-Bug, SPA-Fallback, Auth-Middleware, Cron, Mailer, Crypto - Routen: OAuth-State, Email/Stripe/Analytics; client/.env.example Made-with: Cursor
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { Client, Databases, Query, ID } from 'node-appwrite'
|
||||
import { config } from '../config/index.mjs'
|
||||
import { config, isAdmin } from '../config/index.mjs'
|
||||
import { NotFoundError } from '../middleware/errorHandler.mjs'
|
||||
|
||||
// Initialize Appwrite client
|
||||
@@ -236,22 +236,26 @@ export const emailStats = {
|
||||
},
|
||||
|
||||
async resetDaily() {
|
||||
// Reset daily counters - would be called by a cron job
|
||||
const allStats = await db.list(Collections.EMAIL_STATS, [])
|
||||
let n = 0
|
||||
for (const stat of allStats) {
|
||||
await db.update(Collections.EMAIL_STATS, stat.$id, { todaySorted: 0 })
|
||||
n++
|
||||
}
|
||||
return n
|
||||
},
|
||||
|
||||
async resetWeekly() {
|
||||
// Reset weekly counters - would be called by a cron job
|
||||
const allStats = await db.list(Collections.EMAIL_STATS, [])
|
||||
let n = 0
|
||||
for (const stat of allStats) {
|
||||
await db.update(Collections.EMAIL_STATS, stat.$id, {
|
||||
await db.update(Collections.EMAIL_STATS, stat.$id, {
|
||||
weekSorted: 0,
|
||||
categoriesJson: '{}',
|
||||
})
|
||||
n++
|
||||
}
|
||||
return n
|
||||
},
|
||||
}
|
||||
|
||||
@@ -299,42 +303,60 @@ export const emailUsage = {
|
||||
* Subscriptions operations
|
||||
*/
|
||||
export const subscriptions = {
|
||||
async getByUser(userId) {
|
||||
/**
|
||||
* @param {string} userId
|
||||
* @param {string|null} [viewerEmail] - if set and isAdmin(email), effective plan is business (highest tier)
|
||||
*/
|
||||
async getByUser(userId, viewerEmail = null) {
|
||||
const subscription = await db.findOne(Collections.SUBSCRIPTIONS, [Query.equal('userId', userId)])
|
||||
|
||||
|
||||
let result
|
||||
|
||||
// If no subscription, user is on free tier
|
||||
if (!subscription) {
|
||||
const usage = await emailUsage.getUsage(userId)
|
||||
return {
|
||||
result = {
|
||||
plan: 'free',
|
||||
status: 'active',
|
||||
isFreeTier: true,
|
||||
emailsUsedThisMonth: usage.emailsProcessed,
|
||||
emailsLimit: 500, // From config
|
||||
}
|
||||
} else {
|
||||
// Check if subscription is active
|
||||
const isActive = subscription.status === 'active'
|
||||
const isFreeTier = !isActive || subscription.plan === 'free'
|
||||
|
||||
// Get usage for free tier users
|
||||
let emailsUsedThisMonth = 0
|
||||
let emailsLimit = -1 // Unlimited for paid
|
||||
|
||||
if (isFreeTier) {
|
||||
const usage = await emailUsage.getUsage(userId)
|
||||
emailsUsedThisMonth = usage.emailsProcessed
|
||||
emailsLimit = 500 // From config
|
||||
}
|
||||
|
||||
result = {
|
||||
...subscription,
|
||||
plan: subscription.plan || 'free',
|
||||
isFreeTier,
|
||||
emailsUsedThisMonth,
|
||||
emailsLimit,
|
||||
}
|
||||
}
|
||||
|
||||
// Check if subscription is active
|
||||
const isActive = subscription.status === 'active'
|
||||
const isFreeTier = !isActive || subscription.plan === 'free'
|
||||
|
||||
// Get usage for free tier users
|
||||
let emailsUsedThisMonth = 0
|
||||
let emailsLimit = -1 // Unlimited for paid
|
||||
|
||||
if (isFreeTier) {
|
||||
const usage = await emailUsage.getUsage(userId)
|
||||
emailsUsedThisMonth = usage.emailsProcessed
|
||||
emailsLimit = 500 // From config
|
||||
if (viewerEmail && isAdmin(viewerEmail)) {
|
||||
return {
|
||||
...result,
|
||||
plan: config.topSubscriptionPlan,
|
||||
status: 'active',
|
||||
isFreeTier: false,
|
||||
emailsLimit: -1,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...subscription,
|
||||
plan: subscription.plan || 'free',
|
||||
isFreeTier,
|
||||
emailsUsedThisMonth,
|
||||
emailsLimit,
|
||||
}
|
||||
return result
|
||||
},
|
||||
|
||||
async getByStripeId(stripeSubscriptionId) {
|
||||
@@ -352,8 +374,8 @@ export const subscriptions = {
|
||||
},
|
||||
|
||||
async upsertByUser(userId, data) {
|
||||
const existing = await this.getByUser(userId)
|
||||
if (existing) {
|
||||
const existing = await db.findOne(Collections.SUBSCRIPTIONS, [Query.equal('userId', userId)])
|
||||
if (existing?.$id) {
|
||||
return this.update(existing.$id, data)
|
||||
}
|
||||
return this.create({ userId, ...data })
|
||||
@@ -377,6 +399,12 @@ export const userPreferences = {
|
||||
autoDetectCompanies: true,
|
||||
version: 1,
|
||||
categoryAdvanced: {},
|
||||
profile: {
|
||||
displayName: '',
|
||||
timezone: '',
|
||||
notificationPrefs: {},
|
||||
},
|
||||
cleanupMeta: {},
|
||||
cleanup: {
|
||||
enabled: false,
|
||||
readItems: {
|
||||
@@ -413,6 +441,9 @@ export const userPreferences = {
|
||||
companyLabels: preferences.companyLabels || defaults.companyLabels,
|
||||
nameLabels: preferences.nameLabels || defaults.nameLabels,
|
||||
autoDetectCompanies: preferences.autoDetectCompanies !== undefined ? preferences.autoDetectCompanies : defaults.autoDetectCompanies,
|
||||
profile: preferences.profile != null ? { ...defaults.profile, ...preferences.profile } : defaults.profile,
|
||||
cleanupMeta:
|
||||
preferences.cleanupMeta !== undefined ? preferences.cleanupMeta : defaults.cleanupMeta,
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user