1+ import bcrypt from 'bcrypt'
2+ import { prisma } from '@/lib/prisma'
3+ import { logger } from '@/lib/logger'
4+ import { z } from 'zod'
5+
6+ export const migrateUserSchema = z . object ( {
7+ id : z . string ( ) . optional ( ) ,
8+ email : z . string ( ) . email ( 'Invalid email address' ) ,
9+ password : z . string ( ) . min ( 1 , 'Password is required' ) ,
10+ name : z . string ( ) . optional ( ) ,
11+ avatar : z . string ( ) . optional ( ) ,
12+ isPremium : z . boolean ( ) . optional ( ) . default ( false ) ,
13+ isPremiumDrop : z . boolean ( ) . optional ( ) . default ( false ) ,
14+ isPremiumMails : z . boolean ( ) . optional ( ) . default ( false ) ,
15+ isPremiumVault : z . boolean ( ) . optional ( ) . default ( false ) ,
16+ isPremiumDB : z . boolean ( ) . optional ( ) . default ( false ) ,
17+ premiumTierDrop : z . string ( ) . optional ( ) . default ( 'free' ) ,
18+ premiumTierMails : z . string ( ) . optional ( ) . default ( 'free' ) ,
19+ premiumTierVault : z . string ( ) . optional ( ) . default ( 'free' ) ,
20+ premiumTierDB : z . string ( ) . optional ( ) . default ( 'free' ) ,
21+ twoFactorEnabled : z . boolean ( ) . optional ( ) . default ( false ) ,
22+ twoFactorSecret : z . string ( ) . optional ( ) ,
23+ customStorageLimit : z . number ( ) . optional ( ) ,
24+ customApiKeyLimit : z . number ( ) . optional ( ) ,
25+ isNullDropTeam : z . boolean ( ) . optional ( ) . default ( false ) ,
26+ accessFilesPreview : z . boolean ( ) . optional ( ) . default ( false ) ,
27+ accessFilesDownload : z . boolean ( ) . optional ( ) . default ( false ) ,
28+ nullDropTeamRole : z . string ( ) . optional ( ) . default ( 'member' ) ,
29+ customDomain : z . string ( ) . optional ( ) ,
30+ customDomainVerified : z . boolean ( ) . optional ( ) . default ( false ) ,
31+ polarCustomerId : z . string ( ) . optional ( ) ,
32+ polarSubscriptionId : z . string ( ) . optional ( ) ,
33+ polarSubscriptionStatus : z . string ( ) . optional ( ) ,
34+ createdAt : z . string ( ) . datetime ( ) . optional ( ) ,
35+ } )
36+
37+ export type MigrateUserData = z . infer < typeof migrateUserSchema >
38+
39+ export async function migrateUser ( data : MigrateUserData ) : Promise < { success : boolean ; userId : string ; email : string ; message : string } > {
40+ logger . ups ( 'Migration attempt for user:' , data . email )
41+
42+ let existingUser = null
43+ if ( data . id ) {
44+ existingUser = await prisma . user . findUnique ( {
45+ where : { id : data . id } ,
46+ } )
47+ }
48+
49+ if ( ! existingUser ) {
50+ existingUser = await prisma . user . findUnique ( {
51+ where : { email : data . email } ,
52+ } )
53+ }
54+
55+ if ( existingUser ) {
56+ if ( existingUser . migraited ) {
57+ logger . warn ( 'User already migrated:' , data . email )
58+ return {
59+ success : false ,
60+ userId : existingUser . id ,
61+ email : existingUser . email ,
62+ message : 'User already migrated' ,
63+ }
64+ }
65+ logger . info ( 'Updating existing user during migration:' , data . email )
66+
67+ let passwordHash : string
68+ if ( data . password . startsWith ( '$2b$' ) ) {
69+ passwordHash = data . password
70+ logger . info ( 'Using existing hashed password for user:' , data . email )
71+ } else {
72+ logger . info ( 'Hashing new password for user:' , data . email )
73+ passwordHash = await bcrypt . hash ( data . password , 10 )
74+ logger . info ( 'Password hashed successfully for user:' , data . email )
75+ }
76+
77+ logger . info ( 'Attempting to update user in database:' , existingUser . id )
78+ const user = await prisma . user . update ( {
79+ where : { id : existingUser . id } ,
80+ data : {
81+ passwordHash,
82+ displayName : data . name ,
83+ avatar : data . avatar ,
84+ twoFactorEnabled : data . twoFactorEnabled ,
85+ twoFactorSecret : data . twoFactorSecret ,
86+ migraited : true ,
87+ createdAt : data . createdAt ? new Date ( data . createdAt ) : undefined ,
88+ } ,
89+ } )
90+ logger . info ( 'User updated successfully in database:' , user . id , user . email )
91+
92+ logger . info ( 'Calling migrateServiceEntitlements for updated user:' , user . id )
93+ await migrateServiceEntitlements ( user . id , data )
94+ logger . info ( 'migrateServiceEntitlements completed for updated user:' , user . id )
95+
96+ logger . info ( 'User migrated (updated):' , user . id , user . email )
97+
98+ return {
99+ success : true ,
100+ userId : user . id ,
101+ email : user . email ,
102+ message : 'User migrated successfully' ,
103+ }
104+ }
105+
106+ let passwordHash : string
107+ if ( data . password . startsWith ( '$2b$' ) ) {
108+ passwordHash = data . password
109+ logger . info ( 'Using existing hashed password for new user:' , data . email )
110+ } else {
111+ logger . info ( 'Hashing new password for new user:' , data . email )
112+ passwordHash = await bcrypt . hash ( data . password , 10 )
113+ logger . info ( 'Password hashed successfully for new user:' , data . email )
114+ }
115+
116+ logger . info ( 'Attempting to create new user in database:' , data . email )
117+ const user = await prisma . user . create ( {
118+ data : {
119+ id : data . id ,
120+ email : data . email ,
121+ passwordHash,
122+ displayName : data . name ,
123+ avatar : data . avatar ,
124+ twoFactorEnabled : data . twoFactorEnabled ,
125+ twoFactorSecret : data . twoFactorSecret ,
126+ migraited : true ,
127+ createdAt : data . createdAt ? new Date ( data . createdAt ) : undefined ,
128+ } ,
129+ } )
130+ logger . info ( 'New user created successfully in database:' , user . id , user . email )
131+
132+ logger . info ( 'Calling migrateServiceEntitlements for new user:' , user . id )
133+ await migrateServiceEntitlements ( user . id , data )
134+ logger . info ( 'migrateServiceEntitlements completed for new user:' , user . id )
135+
136+ logger . info ( 'User migrated (created):' , user . id , user . email )
137+
138+ return {
139+ success : true ,
140+ userId : user . id ,
141+ email : user . email ,
142+ message : 'User migrated successfully' ,
143+ }
144+ }
145+
146+ async function migrateServiceEntitlements (
147+ userId : string ,
148+ data : MigrateUserData
149+ ) {
150+ const dropAccessFlags : Record < string , any > = { }
151+ if ( data . isNullDropTeam ) {
152+ dropAccessFlags . isNullDropTeam = true
153+ dropAccessFlags . nullDropTeamRole = data . nullDropTeamRole
154+ }
155+ if ( data . accessFilesPreview ) {
156+ dropAccessFlags . accessFilesPreview = true
157+ }
158+ if ( data . accessFilesDownload ) {
159+ dropAccessFlags . accessFilesDownload = true
160+ }
161+
162+ const dropMetadata : Record < string , any > = { }
163+ if ( data . customDomain ) {
164+ dropMetadata . customDomain = data . customDomain
165+ dropMetadata . customDomainVerified = data . customDomainVerified
166+ }
167+
168+ logger . info ( 'Upserting DROP service entitlement for userId:' , userId )
169+ await prisma . userServiceEntitlement . upsert ( {
170+ where : {
171+ userId_service : {
172+ userId,
173+ service : 'DROP' ,
174+ } ,
175+ } ,
176+ create : {
177+ userId,
178+ service : 'DROP' ,
179+ tier : data . premiumTierDrop || 'free' ,
180+ isPremium : data . isPremiumDrop || data . isPremium || false ,
181+ accessFlags : Object . keys ( dropAccessFlags ) . length > 0 ? dropAccessFlags : undefined ,
182+ metadata : Object . keys ( dropMetadata ) . length > 0 ? dropMetadata : undefined ,
183+ customStorageLimit : data . customStorageLimit ,
184+ customApiKeyLimit : data . customApiKeyLimit ,
185+ polarCustomerId : data . polarCustomerId ,
186+ polarSubscriptionId : data . polarSubscriptionId ,
187+ polarSubscriptionStatus : data . polarSubscriptionStatus ,
188+ } ,
189+ update : {
190+ tier : data . premiumTierDrop || 'free' ,
191+ isPremium : data . isPremiumDrop || data . isPremium || false ,
192+ accessFlags : Object . keys ( dropAccessFlags ) . length > 0 ? dropAccessFlags : undefined ,
193+ metadata : Object . keys ( dropMetadata ) . length > 0 ? dropMetadata : undefined ,
194+ customStorageLimit : data . customStorageLimit ,
195+ customApiKeyLimit : data . customApiKeyLimit ,
196+ polarCustomerId : data . polarCustomerId ,
197+ polarSubscriptionId : data . polarSubscriptionId ,
198+ polarSubscriptionStatus : data . polarSubscriptionStatus ,
199+ } ,
200+ } )
201+ logger . info ( 'DROP service entitlement upserted for userId:' , userId )
202+
203+ if ( data . isPremiumMails || data . isPremium ) {
204+ logger . info ( 'Upserting MAILS service entitlement for userId:' , userId )
205+ await prisma . userServiceEntitlement . upsert ( {
206+ where : {
207+ userId_service : {
208+ userId,
209+ service : 'MAILS' ,
210+ } ,
211+ } ,
212+ create : {
213+ userId,
214+ service : 'MAILS' ,
215+ tier : data . premiumTierMails || 'free' ,
216+ isPremium : data . isPremiumMails || data . isPremium || false ,
217+ } ,
218+ update : {
219+ tier : data . premiumTierMails || 'free' ,
220+ isPremium : data . isPremiumMails || data . isPremium || false ,
221+ } ,
222+ } )
223+ logger . info ( 'MAILS service entitlement upserted for userId:' , userId )
224+ }
225+
226+ if ( data . isPremiumVault || data . isPremium ) {
227+ logger . info ( 'Upserting VAULT service entitlement for userId:' , userId )
228+ await prisma . userServiceEntitlement . upsert ( {
229+ where : {
230+ userId_service : {
231+ userId,
232+ service : 'VAULT' ,
233+ } ,
234+ } ,
235+ create : {
236+ userId,
237+ service : 'VAULT' ,
238+ tier : data . premiumTierVault || 'free' ,
239+ isPremium : data . isPremiumVault || data . isPremium || false ,
240+ } ,
241+ update : {
242+ tier : data . premiumTierVault || 'free' ,
243+ isPremium : data . isPremiumVault || data . isPremium || false ,
244+ } ,
245+ } )
246+ logger . info ( 'VAULT service entitlement upserted for userId:' , userId )
247+ }
248+
249+ if ( data . isPremiumDB || data . isPremium ) {
250+ logger . info ( 'Upserting DB service entitlement for userId:' , userId )
251+ await prisma . userServiceEntitlement . upsert ( {
252+ where : {
253+ userId_service : {
254+ userId,
255+ service : 'DB' ,
256+ } ,
257+ } ,
258+ create : {
259+ userId,
260+ service : 'DB' ,
261+ tier : data . premiumTierDB || 'free' ,
262+ isPremium : data . isPremiumDB || data . isPremium || false
263+ } ,
264+ update : {
265+ tier : data . premiumTierDB || 'free' ,
266+ isPremium : data . isPremiumDB || data . isPremium || false
267+ } ,
268+ } )
269+ logger . info ( 'DB service entitlement upserted for userId:' , userId )
270+ }
271+ }
0 commit comments