Files
Emailsorter/server/services/database.mjs
ANDJ abf761db07 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
2026-01-22 19:32:12 +01:00

429 lines
11 KiB
JavaScript

/**
* Database Service
* Centralized Appwrite database operations
*/
import { Client, Databases, Query, ID } from 'node-appwrite'
import { config } from '../config/index.mjs'
import { NotFoundError } from '../middleware/errorHandler.mjs'
// Initialize Appwrite client
const client = new Client()
.setEndpoint(config.appwrite.endpoint)
.setProject(config.appwrite.projectId)
.setKey(config.appwrite.apiKey)
const databases = new Databases(client)
const DB_ID = config.appwrite.databaseId
/**
* Collection names
*/
export const Collections = {
PRODUCTS: 'products',
QUESTIONS: 'questions',
SUBMISSIONS: 'submissions',
ANSWERS: 'answers',
ORDERS: 'orders',
EMAIL_ACCOUNTS: 'email_accounts',
EMAIL_STATS: 'email_stats',
EMAIL_DIGESTS: 'email_digests',
SUBSCRIPTIONS: 'subscriptions',
USER_PREFERENCES: 'user_preferences',
}
/**
* Generic database operations
*/
export const db = {
/**
* Create a document
*/
async create(collection, data, id = ID.unique()) {
return await databases.createDocument(DB_ID, collection, id, data)
},
/**
* Get a document by ID
*/
async get(collection, id) {
try {
return await databases.getDocument(DB_ID, collection, id)
} catch (error) {
if (error.code === 404) {
throw new NotFoundError(collection)
}
throw error
}
},
/**
* Update a document
*/
async update(collection, id, data) {
return await databases.updateDocument(DB_ID, collection, id, data)
},
/**
* Delete a document
*/
async delete(collection, id) {
return await databases.deleteDocument(DB_ID, collection, id)
},
/**
* List documents with optional queries
*/
async list(collection, queries = []) {
const response = await databases.listDocuments(DB_ID, collection, queries)
return response.documents
},
/**
* Find one document matching queries
*/
async findOne(collection, queries) {
const docs = await this.list(collection, [...queries, Query.limit(1)])
return docs[0] || null
},
/**
* Check if document exists
*/
async exists(collection, id) {
try {
await databases.getDocument(DB_ID, collection, id)
return true
} catch {
return false
}
},
/**
* Count documents
*/
async count(collection, queries = []) {
const response = await databases.listDocuments(DB_ID, collection, [
...queries,
Query.limit(1),
])
return response.total
},
}
/**
* Product operations
*/
export const products = {
async getBySlug(slug) {
return db.findOne(Collections.PRODUCTS, [
Query.equal('slug', slug),
Query.equal('isActive', true),
])
},
async getActive() {
return db.list(Collections.PRODUCTS, [Query.equal('isActive', true)])
},
}
/**
* Questions operations
*/
export const questions = {
async getByProduct(productId) {
return db.list(Collections.QUESTIONS, [
Query.equal('productId', productId),
Query.equal('isActive', true),
Query.orderAsc('step'),
Query.orderAsc('order'),
])
},
}
/**
* Submissions operations
*/
export const submissions = {
async create(data) {
return db.create(Collections.SUBMISSIONS, data)
},
async updateStatus(id, status) {
return db.update(Collections.SUBMISSIONS, id, { status })
},
async getByUser(userId) {
return db.list(Collections.SUBMISSIONS, [Query.equal('userId', userId)])
},
}
/**
* Email accounts operations
*/
export const emailAccounts = {
async create(data) {
return db.create(Collections.EMAIL_ACCOUNTS, data)
},
async getByUser(userId) {
return db.list(Collections.EMAIL_ACCOUNTS, [
Query.equal('userId', userId),
Query.equal('isActive', true),
])
},
async get(id) {
return db.get(Collections.EMAIL_ACCOUNTS, id)
},
async updateLastSync(id) {
return db.update(Collections.EMAIL_ACCOUNTS, id, {
lastSync: new Date().toISOString(),
})
},
async deactivate(id) {
return db.update(Collections.EMAIL_ACCOUNTS, id, { isActive: false })
},
}
/**
* Email stats operations
*/
export const emailStats = {
async getByUser(userId) {
return db.findOne(Collections.EMAIL_STATS, [Query.equal('userId', userId)])
},
async create(userId, data) {
return db.create(Collections.EMAIL_STATS, { userId, ...data })
},
async increment(userId, counts) {
const stats = await this.getByUser(userId)
if (stats) {
return db.update(Collections.EMAIL_STATS, stats.$id, {
totalSorted: (stats.totalSorted || 0) + (counts.total || 0),
todaySorted: (stats.todaySorted || 0) + (counts.today || 0),
weekSorted: (stats.weekSorted || 0) + (counts.week || 0),
timeSavedMinutes: (stats.timeSavedMinutes || 0) + (counts.timeSaved || 0),
})
} else {
return this.create(userId, {
totalSorted: counts.total || 0,
todaySorted: counts.today || 0,
weekSorted: counts.week || 0,
timeSavedMinutes: counts.timeSaved || 0,
categoriesJson: '{}',
})
}
},
async updateCategories(userId, categories) {
const stats = await this.getByUser(userId)
if (stats) {
return db.update(Collections.EMAIL_STATS, stats.$id, {
categoriesJson: JSON.stringify(categories),
})
}
return null
},
async resetDaily() {
// Reset daily counters - would be called by a cron job
const allStats = await db.list(Collections.EMAIL_STATS, [])
for (const stat of allStats) {
await db.update(Collections.EMAIL_STATS, stat.$id, { todaySorted: 0 })
}
},
async resetWeekly() {
// Reset weekly counters - would be called by a cron job
const allStats = await db.list(Collections.EMAIL_STATS, [])
for (const stat of allStats) {
await db.update(Collections.EMAIL_STATS, stat.$id, {
weekSorted: 0,
categoriesJson: '{}',
})
}
},
}
/**
* Subscriptions operations
*/
export const subscriptions = {
async getByUser(userId) {
return db.findOne(Collections.SUBSCRIPTIONS, [Query.equal('userId', userId)])
},
async getByStripeId(stripeSubscriptionId) {
return db.findOne(Collections.SUBSCRIPTIONS, [
Query.equal('stripeSubscriptionId', stripeSubscriptionId),
])
},
async create(data) {
return db.create(Collections.SUBSCRIPTIONS, data)
},
async update(id, data) {
return db.update(Collections.SUBSCRIPTIONS, id, data)
},
async upsertByUser(userId, data) {
const existing = await this.getByUser(userId)
if (existing) {
return this.update(existing.$id, data)
}
return this.create({ userId, ...data })
},
}
/**
* User preferences operations
*/
export const userPreferences = {
async getByUser(userId) {
const pref = await db.findOne(Collections.USER_PREFERENCES, [
Query.equal('userId', userId),
])
if (pref?.preferencesJson) {
return { ...pref, preferences: JSON.parse(pref.preferencesJson) }
}
return pref
},
async upsert(userId, preferences) {
const existing = await db.findOne(Collections.USER_PREFERENCES, [
Query.equal('userId', userId),
])
const data = { preferencesJson: JSON.stringify(preferences) }
if (existing) {
return db.update(Collections.USER_PREFERENCES, existing.$id, data)
}
return db.create(Collections.USER_PREFERENCES, { userId, ...data })
},
}
/**
* Orders operations
*/
export const orders = {
async create(submissionId, orderData) {
return db.create(Collections.ORDERS, {
submissionId,
orderDataJson: JSON.stringify(orderData),
})
},
}
/**
* Email digests operations
*/
export const emailDigests = {
async create(userId, digestData) {
const date = new Date().toISOString().split('T')[0] // YYYY-MM-DD
return db.create(Collections.EMAIL_DIGESTS, {
userId,
date,
statsJson: JSON.stringify(digestData.stats || {}),
highlightsJson: JSON.stringify(digestData.highlights || []),
suggestionsJson: JSON.stringify(digestData.suggestions || []),
totalSorted: digestData.totalSorted || 0,
inboxCleared: digestData.inboxCleared || 0,
timeSavedMinutes: digestData.timeSavedMinutes || 0,
createdAt: new Date().toISOString(),
})
},
async getByUserToday(userId) {
const today = new Date().toISOString().split('T')[0]
const digest = await db.findOne(Collections.EMAIL_DIGESTS, [
Query.equal('userId', userId),
Query.equal('date', today),
])
if (digest) {
return {
...digest,
stats: JSON.parse(digest.statsJson || '{}'),
highlights: JSON.parse(digest.highlightsJson || '[]'),
suggestions: JSON.parse(digest.suggestionsJson || '[]'),
}
}
return null
},
async getByUserRecent(userId, days = 7) {
const startDate = new Date()
startDate.setDate(startDate.getDate() - days)
const digests = await db.list(Collections.EMAIL_DIGESTS, [
Query.equal('userId', userId),
Query.greaterThanEqual('date', startDate.toISOString().split('T')[0]),
Query.orderDesc('date'),
])
return digests.map(d => ({
...d,
stats: JSON.parse(d.statsJson || '{}'),
highlights: JSON.parse(d.highlightsJson || '[]'),
suggestions: JSON.parse(d.suggestionsJson || '[]'),
}))
},
async updateToday(userId, updates) {
const today = new Date().toISOString().split('T')[0]
const existing = await db.findOne(Collections.EMAIL_DIGESTS, [
Query.equal('userId', userId),
Query.equal('date', today),
])
if (existing) {
const updateData = {}
if (updates.stats) updateData.statsJson = JSON.stringify(updates.stats)
if (updates.highlights) updateData.highlightsJson = JSON.stringify(updates.highlights)
if (updates.suggestions) updateData.suggestionsJson = JSON.stringify(updates.suggestions)
if (updates.totalSorted !== undefined) {
updateData.totalSorted = (existing.totalSorted || 0) + updates.totalSorted
}
if (updates.inboxCleared !== undefined) {
updateData.inboxCleared = (existing.inboxCleared || 0) + updates.inboxCleared
}
if (updates.timeSavedMinutes !== undefined) {
updateData.timeSavedMinutes = (existing.timeSavedMinutes || 0) + updates.timeSavedMinutes
}
return db.update(Collections.EMAIL_DIGESTS, existing.$id, updateData)
}
// Create new digest for today
return this.create(userId, {
stats: updates.stats || {},
highlights: updates.highlights || [],
suggestions: updates.suggestions || [],
totalSorted: updates.totalSorted || 0,
inboxCleared: updates.inboxCleared || 0,
timeSavedMinutes: updates.timeSavedMinutes || 0,
})
},
}
export default {
db,
products,
questions,
submissions,
emailAccounts,
emailStats,
emailDigests,
subscriptions,
userPreferences,
orders,
Collections,
}