Try
dfssdfsfdsf
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
import { Client, Databases, Query, ID } from 'node-appwrite'
|
||||
import { config, isAdmin } from '../config/index.mjs'
|
||||
import { NotFoundError } from '../middleware/errorHandler.mjs'
|
||||
import { log } from '../middleware/logger.mjs'
|
||||
|
||||
// Initialize Appwrite client
|
||||
const client = new Client()
|
||||
@@ -15,6 +16,19 @@ const client = new Client()
|
||||
|
||||
const databases = new Databases(client)
|
||||
const DB_ID = config.appwrite.databaseId
|
||||
const DATABASE_ID = DB_ID
|
||||
|
||||
/**
|
||||
* Appwrite: database/collection missing (schema not provisioned yet)
|
||||
*/
|
||||
function isCollectionNotFound(err) {
|
||||
if (!err) return false
|
||||
const msg = typeof err.message === 'string' ? err.message : ''
|
||||
return (
|
||||
err.type === 'collection_not_found' ||
|
||||
msg.includes('Collection with the requested ID')
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Collection names
|
||||
@@ -44,7 +58,12 @@ export const db = {
|
||||
* Create a document
|
||||
*/
|
||||
async create(collection, data, id = ID.unique()) {
|
||||
return await databases.createDocument(DB_ID, collection, id, data)
|
||||
try {
|
||||
return await databases.createDocument(DB_ID, collection, id, data)
|
||||
} catch (err) {
|
||||
if (isCollectionNotFound(err)) return null
|
||||
throw err
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -54,6 +73,9 @@ export const db = {
|
||||
try {
|
||||
return await databases.getDocument(DB_ID, collection, id)
|
||||
} catch (error) {
|
||||
if (isCollectionNotFound(error)) {
|
||||
return null
|
||||
}
|
||||
if (error.code === 404) {
|
||||
throw new NotFoundError(collection)
|
||||
}
|
||||
@@ -65,22 +87,43 @@ export const db = {
|
||||
* Update a document
|
||||
*/
|
||||
async update(collection, id, data) {
|
||||
return await databases.updateDocument(DB_ID, collection, id, data)
|
||||
try {
|
||||
return await databases.updateDocument(DB_ID, collection, id, data)
|
||||
} catch (err) {
|
||||
if (isCollectionNotFound(err)) return null
|
||||
throw err
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete a document
|
||||
*/
|
||||
async delete(collection, id) {
|
||||
return await databases.deleteDocument(DB_ID, collection, id)
|
||||
try {
|
||||
return await databases.deleteDocument(DB_ID, collection, id)
|
||||
} catch (err) {
|
||||
if (isCollectionNotFound(err)) return null
|
||||
throw err
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* List documents with optional queries
|
||||
*/
|
||||
async list(collection, queries = []) {
|
||||
const response = await databases.listDocuments(DB_ID, collection, queries)
|
||||
return response.documents
|
||||
async list(collectionId, queries = []) {
|
||||
try {
|
||||
const response = await databases.listDocuments(
|
||||
DATABASE_ID,
|
||||
collectionId,
|
||||
queries
|
||||
)
|
||||
return response.documents
|
||||
} catch (err) {
|
||||
if (isCollectionNotFound(err)) {
|
||||
return []
|
||||
}
|
||||
throw err
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -98,7 +141,8 @@ export const db = {
|
||||
try {
|
||||
await databases.getDocument(DB_ID, collection, id)
|
||||
return true
|
||||
} catch {
|
||||
} catch (err) {
|
||||
if (isCollectionNotFound(err)) return false
|
||||
return false
|
||||
}
|
||||
},
|
||||
@@ -107,11 +151,16 @@ export const db = {
|
||||
* Count documents
|
||||
*/
|
||||
async count(collection, queries = []) {
|
||||
const response = await databases.listDocuments(DB_ID, collection, [
|
||||
...queries,
|
||||
Query.limit(1),
|
||||
])
|
||||
return response.total
|
||||
try {
|
||||
const response = await databases.listDocuments(DB_ID, collection, [
|
||||
...queries,
|
||||
Query.limit(1),
|
||||
])
|
||||
return response.total
|
||||
} catch (err) {
|
||||
if (isCollectionNotFound(err)) return 0
|
||||
throw err
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -208,21 +257,28 @@ export const emailStats = {
|
||||
const stats = await this.getByUser(userId)
|
||||
|
||||
if (stats) {
|
||||
return db.update(Collections.EMAIL_STATS, stats.$id, {
|
||||
const updated = await 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: '{}',
|
||||
})
|
||||
if (updated == null && process.env.NODE_ENV === 'development') {
|
||||
log.warn('emailStats.increment: update skipped (missing collection or document)', { userId })
|
||||
}
|
||||
return updated
|
||||
}
|
||||
const created = await this.create(userId, {
|
||||
totalSorted: counts.total || 0,
|
||||
todaySorted: counts.today || 0,
|
||||
weekSorted: counts.week || 0,
|
||||
timeSavedMinutes: counts.timeSaved || 0,
|
||||
categoriesJson: '{}',
|
||||
})
|
||||
if (created == null && process.env.NODE_ENV === 'development') {
|
||||
log.warn('emailStats.increment: create skipped (missing collection)', { userId })
|
||||
}
|
||||
return created
|
||||
},
|
||||
|
||||
async updateCategories(userId, categories) {
|
||||
@@ -276,18 +332,26 @@ export const emailUsage = {
|
||||
const existing = await this.getCurrentMonth(userId)
|
||||
|
||||
if (existing) {
|
||||
return db.update(Collections.EMAIL_USAGE, existing.$id, {
|
||||
const updated = await db.update(Collections.EMAIL_USAGE, existing.$id, {
|
||||
emailsProcessed: (existing.emailsProcessed || 0) + count,
|
||||
lastReset: new Date().toISOString(),
|
||||
})
|
||||
if (updated == null && process.env.NODE_ENV === 'development') {
|
||||
log.warn('emailUsage.increment: update skipped (missing collection or document)', { userId })
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
return db.create(Collections.EMAIL_USAGE, {
|
||||
const created = await db.create(Collections.EMAIL_USAGE, {
|
||||
userId,
|
||||
month,
|
||||
emailsProcessed: count,
|
||||
lastReset: new Date().toISOString(),
|
||||
})
|
||||
if (created == null && process.env.NODE_ENV === 'development') {
|
||||
log.warn('emailUsage.increment: create skipped (missing collection)', { userId })
|
||||
}
|
||||
return created
|
||||
},
|
||||
|
||||
async getUsage(userId) {
|
||||
@@ -578,6 +642,26 @@ export const onboardingState = {
|
||||
last_updated: new Date().toISOString(),
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset onboarding to initial state (does not delete the document).
|
||||
*/
|
||||
async resetToInitial(userId) {
|
||||
const existing = await db.findOne(Collections.ONBOARDING_STATE, [
|
||||
Query.equal('userId', userId),
|
||||
])
|
||||
const data = {
|
||||
onboarding_step: 'not_started',
|
||||
completed_steps_json: JSON.stringify([]),
|
||||
skipped_at: null,
|
||||
first_value_seen_at: null,
|
||||
last_updated: new Date().toISOString(),
|
||||
}
|
||||
if (existing) {
|
||||
return db.update(Collections.ONBOARDING_STATE, existing.$id, data)
|
||||
}
|
||||
return db.create(Collections.ONBOARDING_STATE, { userId, ...data })
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -609,12 +693,17 @@ export const referrals = {
|
||||
attempts++
|
||||
}
|
||||
|
||||
return db.create(Collections.REFERRALS, {
|
||||
const created = await db.create(Collections.REFERRALS, {
|
||||
userId,
|
||||
referralCode: uniqueCode,
|
||||
referralCount: 0,
|
||||
createdAt: new Date().toISOString(),
|
||||
})
|
||||
|
||||
// Collection missing → return null safely
|
||||
if (!created) return null
|
||||
|
||||
return created
|
||||
},
|
||||
|
||||
async getByCode(code) {
|
||||
@@ -746,6 +835,25 @@ export const emailDigests = {
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all documents in a collection where attribute `userId` matches (paginated batches of 100).
|
||||
*/
|
||||
export async function deleteAllDocumentsForUser(collection, userId) {
|
||||
let deleted = 0
|
||||
for (;;) {
|
||||
const batch = await db.list(collection, [
|
||||
Query.equal('userId', userId),
|
||||
Query.limit(100),
|
||||
])
|
||||
if (!batch.length) break
|
||||
for (const doc of batch) {
|
||||
await db.delete(collection, doc.$id)
|
||||
deleted++
|
||||
}
|
||||
}
|
||||
return deleted
|
||||
}
|
||||
|
||||
export { Query }
|
||||
|
||||
export default {
|
||||
|
||||
Reference in New Issue
Block a user