🧐 Add updateUserEmail script
This commit is contained in:
@ -1,80 +1,9 @@
|
|||||||
import { PrismaClient } from '@typebot.io/prisma'
|
import { destroyUser } from './helpers/destroyUser'
|
||||||
import * as p from '@clack/prompts'
|
|
||||||
import { promptAndSetEnvironment } from './utils'
|
import { promptAndSetEnvironment } from './utils'
|
||||||
|
|
||||||
const destroyUser = async () => {
|
const runDestroyUser = async () => {
|
||||||
await promptAndSetEnvironment('production')
|
await promptAndSetEnvironment('production')
|
||||||
|
return destroyUser()
|
||||||
const prisma = new PrismaClient({
|
|
||||||
log: [{ emit: 'event', level: 'query' }, 'info', 'warn', 'error'],
|
|
||||||
})
|
|
||||||
|
|
||||||
prisma.$on('query', (e) => {
|
|
||||||
console.log(e.query)
|
|
||||||
console.log(e.params)
|
|
||||||
console.log(e.duration, 'ms')
|
|
||||||
})
|
|
||||||
|
|
||||||
const email = (await p.text({
|
|
||||||
message: 'User email?',
|
|
||||||
})) as string
|
|
||||||
|
|
||||||
if (!email || typeof email !== 'string') {
|
|
||||||
console.log('No email provided')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const workspaces = await prisma.workspace.findMany({
|
|
||||||
where: {
|
|
||||||
members: { every: { user: { email } } },
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
members: { select: { user: { select: { email: true } }, role: true } },
|
|
||||||
typebots: {
|
|
||||||
select: {
|
|
||||||
results: {
|
|
||||||
select: { id: true },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
console.log(`Found ${workspaces.length} workspaces`)
|
|
||||||
|
|
||||||
const proceed = await p.confirm({ message: 'Proceed?' })
|
|
||||||
if (!proceed || typeof proceed !== 'boolean') {
|
|
||||||
console.log('Aborting')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const workspace of workspaces) {
|
|
||||||
const hasResults = workspace.typebots.some((t) => t.results.length > 0)
|
|
||||||
if (hasResults) {
|
|
||||||
console.log(
|
|
||||||
`Workspace ${workspace.name} has results. Deleting results first...`,
|
|
||||||
workspace.typebots.filter((t) => t.results.length > 0)
|
|
||||||
)
|
|
||||||
console.log(JSON.stringify({ members: workspace.members }, null, 2))
|
|
||||||
const proceed = await p.confirm({ message: 'Proceed?' })
|
|
||||||
if (!proceed || typeof proceed !== 'boolean') {
|
|
||||||
console.log('Aborting')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const typebot of workspace.typebots.filter(
|
|
||||||
(t) => t.results.length > 0
|
|
||||||
)) {
|
|
||||||
for (const result of typebot.results) {
|
|
||||||
await prisma.result.deleteMany({ where: { id: result.id } })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await prisma.workspace.delete({ where: { id: workspace.id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await prisma.user.delete({ where: { email } })
|
|
||||||
|
|
||||||
console.log(`Deleted user ${JSON.stringify(user, null, 2)}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyUser()
|
runDestroyUser()
|
||||||
|
98
packages/scripts/helpers/destroyUser.ts
Normal file
98
packages/scripts/helpers/destroyUser.ts
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import { isCancel, text, confirm } from '@clack/prompts'
|
||||||
|
import { Plan, PrismaClient } from '@typebot.io/prisma'
|
||||||
|
import { writeFileSync } from 'fs'
|
||||||
|
|
||||||
|
export const destroyUser = async (userEmail?: string) => {
|
||||||
|
const prisma = new PrismaClient()
|
||||||
|
|
||||||
|
const email =
|
||||||
|
userEmail ??
|
||||||
|
(await text({
|
||||||
|
message: 'User email?',
|
||||||
|
}))
|
||||||
|
|
||||||
|
if (!email || isCancel(email)) {
|
||||||
|
console.log('No email provided')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const workspaces = await prisma.workspace.findMany({
|
||||||
|
where: {
|
||||||
|
members: { every: { user: { email } } },
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
members: { select: { user: { select: { email: true } }, role: true } },
|
||||||
|
typebots: {
|
||||||
|
select: {
|
||||||
|
results: {
|
||||||
|
select: { id: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(`Found ${workspaces.length} workspaces`)
|
||||||
|
|
||||||
|
if (workspaces.some((w) => w.plan !== Plan.FREE)) {
|
||||||
|
console.log(
|
||||||
|
`Some workspaces have a plan other than FREE. Something is wrong. Logging and exiting...`
|
||||||
|
)
|
||||||
|
writeFileSync(
|
||||||
|
'logs/workspaces-issue.json',
|
||||||
|
JSON.stringify(workspaces, null, 2)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
workspaces.some((w) =>
|
||||||
|
w.members.some((m) => m.user.email && m.user.email !== email)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
console.log(
|
||||||
|
`Some workspaces have other members. Something is wrong. Logging and exiting...`
|
||||||
|
)
|
||||||
|
writeFileSync(
|
||||||
|
'logs/workspaces-issue.json',
|
||||||
|
JSON.stringify(workspaces, null, 2)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('All workspaces have a FREE plan')
|
||||||
|
|
||||||
|
const proceed = await confirm({ message: 'Proceed?' })
|
||||||
|
if (!proceed || typeof proceed !== 'boolean') {
|
||||||
|
console.log('Aborting')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const workspace of workspaces) {
|
||||||
|
const hasResults = workspace.typebots.some((t) => t.results.length > 0)
|
||||||
|
if (hasResults) {
|
||||||
|
console.log(
|
||||||
|
`Workspace ${workspace.name} has results. Deleting results first...`,
|
||||||
|
workspace.typebots.filter((t) => t.results.length > 0)
|
||||||
|
)
|
||||||
|
console.log(JSON.stringify({ members: workspace.members }, null, 2))
|
||||||
|
const proceed = await confirm({ message: 'Proceed?' })
|
||||||
|
if (!proceed || typeof proceed !== 'boolean') {
|
||||||
|
console.log('Aborting')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const typebot of workspace.typebots.filter(
|
||||||
|
(t) => t.results.length > 0
|
||||||
|
)) {
|
||||||
|
for (const result of typebot.results) {
|
||||||
|
await prisma.result.deleteMany({ where: { id: result.id } })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await prisma.workspace.delete({ where: { id: workspace.id } })
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await prisma.user.delete({ where: { email } })
|
||||||
|
|
||||||
|
console.log(`Deleted user ${JSON.stringify(user, null, 2)}`)
|
||||||
|
}
|
@ -1,24 +1,20 @@
|
|||||||
import { PrismaClient } from '@typebot.io/prisma'
|
import { PrismaClient } from '@typebot.io/prisma'
|
||||||
import { promptAndSetEnvironment } from './utils'
|
import { promptAndSetEnvironment } from './utils'
|
||||||
import prompts from 'prompts'
|
import { isCancel, text } from '@clack/prompts'
|
||||||
import { isEmpty } from '@typebot.io/lib'
|
|
||||||
|
|
||||||
const inspectUser = async () => {
|
const inspectUser = async () => {
|
||||||
await promptAndSetEnvironment('production')
|
await promptAndSetEnvironment('production')
|
||||||
const response = await prompts({
|
const email = await text({
|
||||||
type: 'text',
|
|
||||||
name: 'email',
|
|
||||||
message: 'User email',
|
message: 'User email',
|
||||||
})
|
})
|
||||||
|
|
||||||
if (isEmpty(response.email)) process.exit()
|
if (!email || isCancel(email)) process.exit()
|
||||||
const prisma = new PrismaClient({
|
|
||||||
log: [{ emit: 'event', level: 'query' }, 'info', 'warn', 'error'],
|
const prisma = new PrismaClient()
|
||||||
})
|
|
||||||
|
|
||||||
const user = await prisma.user.findFirst({
|
const user = await prisma.user.findFirst({
|
||||||
where: {
|
where: {
|
||||||
email: response.email,
|
email,
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
name: true,
|
name: true,
|
||||||
@ -37,7 +33,7 @@ const inspectUser = async () => {
|
|||||||
plan: true,
|
plan: true,
|
||||||
members: {
|
members: {
|
||||||
where: {
|
where: {
|
||||||
user: { email: { not: response.email } },
|
user: { email: { not: email } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
additionalStorageIndex: true,
|
additionalStorageIndex: true,
|
||||||
@ -67,63 +63,7 @@ const inspectUser = async () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!user) {
|
console.log(JSON.stringify(user, null, 2))
|
||||||
console.log('User not found')
|
|
||||||
process.exit()
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Name:', user.name)
|
|
||||||
console.log('Last activity:', user.lastActivityAt.toLocaleDateString())
|
|
||||||
console.log('Company:', user.company)
|
|
||||||
console.log('Onboarding categories:', user.onboardingCategories)
|
|
||||||
console.log('Total workspaces:', user.workspaces.length)
|
|
||||||
console.log('Workspaces:')
|
|
||||||
|
|
||||||
for (const workspace of user.workspaces) {
|
|
||||||
console.log(' - ID:', workspace.workspace.id)
|
|
||||||
console.log(' Name:', workspace.workspace.name)
|
|
||||||
console.log(' Plan:', workspace.workspace.plan)
|
|
||||||
console.log(' Members:', workspace.workspace.members.length + 1)
|
|
||||||
console.log(
|
|
||||||
' Additional storage:',
|
|
||||||
workspace.workspace.additionalStorageIndex
|
|
||||||
)
|
|
||||||
console.log(' Typebots:', workspace.workspace.typebots.length)
|
|
||||||
|
|
||||||
for (const typebot of workspace.workspace.typebots) {
|
|
||||||
console.log(' - Name:', typebot.name)
|
|
||||||
console.log(' Created:', typebot.createdAt.toLocaleDateString())
|
|
||||||
console.log(
|
|
||||||
' Last updated:',
|
|
||||||
typebot.updatedAt.toLocaleDateString()
|
|
||||||
)
|
|
||||||
console.log(' Risk level:', typebot.riskLevel)
|
|
||||||
console.log(
|
|
||||||
' Public ID:',
|
|
||||||
typebot.publishedTypebot?.typebot.publicId
|
|
||||||
)
|
|
||||||
console.log(
|
|
||||||
' URL:',
|
|
||||||
`https://app.typebot.io/typebots/${typebot.id}/edit`
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!typebot.publishedTypebot) continue
|
|
||||||
|
|
||||||
const totalTraffic = await prisma.result.count({
|
|
||||||
where: {
|
|
||||||
typebotId: typebot.id,
|
|
||||||
isArchived: false,
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
_all: true,
|
|
||||||
hasStarted: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
console.log(' Total traffic:', totalTraffic._all)
|
|
||||||
console.log(' Started:', totalTraffic.hasStarted)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inspectUser()
|
inspectUser()
|
||||||
|
@ -26,7 +26,8 @@
|
|||||||
"inspectTypebot": "tsx inspectTypebot.ts",
|
"inspectTypebot": "tsx inspectTypebot.ts",
|
||||||
"inspectWorkspace": "tsx inspectWorkspace.ts",
|
"inspectWorkspace": "tsx inspectWorkspace.ts",
|
||||||
"getCoupon": "tsx getCoupon.ts",
|
"getCoupon": "tsx getCoupon.ts",
|
||||||
"exportResults": "tsx exportResults.ts"
|
"exportResults": "tsx exportResults.ts",
|
||||||
|
"updateUserEmail": "tsx updateUserEmail.ts"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@typebot.io/emails": "workspace:*",
|
"@typebot.io/emails": "workspace:*",
|
||||||
|
58
packages/scripts/updateUserEmail.ts
Normal file
58
packages/scripts/updateUserEmail.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { PrismaClient } from '@typebot.io/prisma'
|
||||||
|
import { promptAndSetEnvironment } from './utils'
|
||||||
|
import { text, isCancel, confirm } from '@clack/prompts'
|
||||||
|
import { destroyUser } from './helpers/destroyUser'
|
||||||
|
|
||||||
|
const updateUserEmail = async () => {
|
||||||
|
await promptAndSetEnvironment('production')
|
||||||
|
|
||||||
|
const prisma = new PrismaClient()
|
||||||
|
|
||||||
|
const currentUserEmail = await text({
|
||||||
|
message: 'Current email?',
|
||||||
|
})
|
||||||
|
|
||||||
|
const newEmail = await text({
|
||||||
|
message: 'New email?',
|
||||||
|
})
|
||||||
|
|
||||||
|
if (
|
||||||
|
!currentUserEmail ||
|
||||||
|
!newEmail ||
|
||||||
|
isCancel(currentUserEmail) ||
|
||||||
|
isCancel(newEmail)
|
||||||
|
)
|
||||||
|
throw new Error('Invalid emails')
|
||||||
|
|
||||||
|
const existingUserWithNewEmail = await prisma.user.findUnique({
|
||||||
|
where: {
|
||||||
|
email: newEmail,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (existingUserWithNewEmail) {
|
||||||
|
console.log(`User with email ${newEmail} already exists`)
|
||||||
|
console.log(JSON.stringify(existingUserWithNewEmail, null, 2))
|
||||||
|
|
||||||
|
const isDestroying = await confirm({
|
||||||
|
message: 'Would you like to destroy it and update the current user?',
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!isDestroying || isCancel(isDestroying)) return
|
||||||
|
|
||||||
|
await destroyUser(newEmail)
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await prisma.user.update({
|
||||||
|
where: {
|
||||||
|
email: currentUserEmail,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
email: newEmail,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(JSON.stringify(user, null, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
updateUserEmail()
|
Reference in New Issue
Block a user