Try
dfssdfsfdsf
This commit is contained in:
@@ -4,13 +4,34 @@
|
||||
*/
|
||||
|
||||
import express from 'express'
|
||||
import { asyncHandler, NotFoundError, ValidationError } from '../middleware/errorHandler.mjs'
|
||||
import { Client, Users, Query as AppwriteQuery } from 'node-appwrite'
|
||||
import { asyncHandler, NotFoundError, ValidationError, AuthorizationError } from '../middleware/errorHandler.mjs'
|
||||
import { validate, schemas, rules } from '../middleware/validate.mjs'
|
||||
import { respond } from '../utils/response.mjs'
|
||||
import { products, questions, submissions, orders, onboardingState, emailAccounts, emailStats, emailDigests, userPreferences, subscriptions, emailUsage, referrals, db, Collections, Query } from '../services/database.mjs'
|
||||
import {
|
||||
products,
|
||||
questions,
|
||||
submissions,
|
||||
orders,
|
||||
onboardingState,
|
||||
emailAccounts,
|
||||
emailStats,
|
||||
emailDigests,
|
||||
userPreferences,
|
||||
subscriptions,
|
||||
emailUsage,
|
||||
referrals,
|
||||
db,
|
||||
Collections,
|
||||
Query,
|
||||
deleteAllDocumentsForUser,
|
||||
} from '../services/database.mjs'
|
||||
import Stripe from 'stripe'
|
||||
import { config } from '../config/index.mjs'
|
||||
import { config, isAdmin } from '../config/index.mjs'
|
||||
import { log } from '../middleware/logger.mjs'
|
||||
import { parseImapAccountAccess } from './email.mjs'
|
||||
import { ImapService } from '../services/imap.mjs'
|
||||
import { isAppwriteCollectionMissing } from '../utils/appwriteErrors.mjs'
|
||||
import { requireAuth } from '../middleware/auth.mjs'
|
||||
|
||||
const router = express.Router()
|
||||
@@ -328,18 +349,31 @@ router.delete('/account/delete',
|
||||
/**
|
||||
* GET /api/referrals/code
|
||||
* Get or create referral code for user
|
||||
* (Also registered on app in index.mjs before router mount.)
|
||||
*/
|
||||
router.get('/referrals/code',
|
||||
requireAuth,
|
||||
asyncHandler(async (req, res) => {
|
||||
const userId = req.appwriteUser.id
|
||||
const referral = await referrals.getOrCreateCode(userId)
|
||||
respond.success(res, {
|
||||
referralCode: referral.referralCode,
|
||||
referralCount: referral.referralCount || 0,
|
||||
export async function handleGetReferralCode(req, res) {
|
||||
const userId = req.appwriteUser.id
|
||||
try {
|
||||
const result = await referrals.getOrCreateCode(userId)
|
||||
if (!result) {
|
||||
return respond.success(res, { referralCode: null, referralCount: 0 })
|
||||
}
|
||||
return respond.success(res, {
|
||||
referralCode: result.referralCode,
|
||||
referralCount: result.referralCount || 0,
|
||||
})
|
||||
})
|
||||
)
|
||||
} catch (err) {
|
||||
if (isAppwriteCollectionMissing(err)) {
|
||||
return respond.success(res, {
|
||||
referralCode: null,
|
||||
referralCount: 0,
|
||||
})
|
||||
}
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
router.get('/referrals/code', requireAuth, asyncHandler(handleGetReferralCode))
|
||||
|
||||
/**
|
||||
* POST /api/referrals/track
|
||||
@@ -383,4 +417,105 @@ router.post('/referrals/track',
|
||||
})
|
||||
)
|
||||
|
||||
async function resolveUserIdByEmail(email) {
|
||||
const normalized = String(email).trim().toLowerCase()
|
||||
const c = new Client()
|
||||
.setEndpoint(config.appwrite.endpoint)
|
||||
.setProject(config.appwrite.projectId)
|
||||
.setKey(config.appwrite.apiKey)
|
||||
const users = new Users(c)
|
||||
try {
|
||||
const res = await users.list([AppwriteQuery.equal('email', normalized)])
|
||||
const uid = res.users?.[0]?.$id
|
||||
if (uid) return uid
|
||||
} catch (e) {
|
||||
log.warn('resolveUserIdByEmail: Users.list failed', { email: normalized, error: e.message })
|
||||
}
|
||||
const acc = await db.findOne(Collections.EMAIL_ACCOUNTS, [Query.equal('email', normalized)])
|
||||
return acc?.userId || null
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/admin/reset-user-sort-data
|
||||
* Admin only: clear sort-related data for a user (by email).
|
||||
*/
|
||||
router.post(
|
||||
'/admin/reset-user-sort-data',
|
||||
requireAuth,
|
||||
validate({
|
||||
body: {
|
||||
email: [rules.required('email'), rules.email()],
|
||||
},
|
||||
}),
|
||||
asyncHandler(async (req, res) => {
|
||||
if (!isAdmin(req.appwriteUser?.email)) {
|
||||
throw new AuthorizationError('Admin access required')
|
||||
}
|
||||
const targetEmail = String(req.body.email).trim().toLowerCase()
|
||||
const targetUserId = await resolveUserIdByEmail(targetEmail)
|
||||
if (!targetUserId) {
|
||||
throw new NotFoundError('User for this email')
|
||||
}
|
||||
|
||||
const statsDoc = await emailStats.getByUser(targetUserId)
|
||||
let statsDeleted = 0
|
||||
if (statsDoc?.$id) {
|
||||
await db.delete(Collections.EMAIL_STATS, statsDoc.$id)
|
||||
statsDeleted = 1
|
||||
}
|
||||
|
||||
const digestsDeleted = await deleteAllDocumentsForUser(Collections.EMAIL_DIGESTS, targetUserId)
|
||||
const usageDeleted = await deleteAllDocumentsForUser(Collections.EMAIL_USAGE, targetUserId)
|
||||
|
||||
await onboardingState.resetToInitial(targetUserId)
|
||||
|
||||
let imapCleared = 0
|
||||
const imapAccounts = await db.list(Collections.EMAIL_ACCOUNTS, [
|
||||
Query.equal('userId', targetUserId),
|
||||
Query.equal('provider', 'imap'),
|
||||
])
|
||||
for (const acc of imapAccounts) {
|
||||
if (!acc.accessToken) continue
|
||||
try {
|
||||
const cfg = parseImapAccountAccess(acc)
|
||||
if (!cfg.password) continue
|
||||
const imap = new ImapService({
|
||||
host: cfg.host,
|
||||
port: cfg.port,
|
||||
secure: cfg.secure,
|
||||
user: acc.email,
|
||||
password: cfg.password,
|
||||
})
|
||||
const n = await imap.removeAllSortedFlags()
|
||||
imapCleared += n
|
||||
} catch (e) {
|
||||
log.warn('reset-user-sort-data: IMAP flags failed for account', {
|
||||
accountId: acc.$id,
|
||||
error: e.message,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
log.info('Admin reset-user-sort-data', {
|
||||
admin: req.appwriteUser.email,
|
||||
targetEmail,
|
||||
targetUserId,
|
||||
statsDeleted,
|
||||
digestsDeleted,
|
||||
usageDeleted,
|
||||
imapCleared,
|
||||
})
|
||||
|
||||
respond.success(res, {
|
||||
reset: true,
|
||||
deleted: {
|
||||
stats: statsDeleted,
|
||||
digests: digestsDeleted,
|
||||
usage: usageDeleted,
|
||||
},
|
||||
imapCleared,
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
export default router
|
||||
|
||||
Reference in New Issue
Block a user