feat: privacy policy + download data & delete account
This commit is contained in:
parent
95df713b06
commit
fc5c16e6dd
14 changed files with 4159 additions and 52 deletions
|
|
@ -21,7 +21,7 @@ export const user = pgTable("user", {
|
|||
}).notNull().default("10000.00000000"), // 10,000 *BUSS
|
||||
bio: varchar("bio", { length: 160 }).default("Hello am 48 year old man from somalia. Sorry for my bed england. I selled my wife for internet connection for play “conter stirk”"),
|
||||
username: varchar("username", { length: 30 }).notNull().unique(),
|
||||
|
||||
|
||||
volumeMaster: decimal("volume_master", { precision: 3, scale: 2 }).notNull().default("0.70"),
|
||||
volumeMuted: boolean("volume_muted").notNull().default(false),
|
||||
|
||||
|
|
@ -103,7 +103,7 @@ export const userPortfolio = pgTable("user_portfolio", {
|
|||
|
||||
export const transaction = pgTable("transaction", {
|
||||
id: serial("id").primaryKey(),
|
||||
userId: integer("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
||||
userId: integer("user_id").references(() => user.id, { onDelete: "set null" }),
|
||||
coinId: integer("coin_id").notNull().references(() => coin.id, { onDelete: "cascade" }),
|
||||
type: transactionTypeEnum("type").notNull(),
|
||||
quantity: decimal("quantity", { precision: 30, scale: 8 }).notNull(),
|
||||
|
|
@ -121,7 +121,7 @@ export const priceHistory = pgTable("price_history", {
|
|||
|
||||
export const comment = pgTable("comment", {
|
||||
id: serial("id").primaryKey(),
|
||||
userId: integer("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
||||
userId: integer("user_id").references(() => user.id, { onDelete: "set null" }),
|
||||
coinId: integer("coin_id").notNull().references(() => coin.id, { onDelete: "cascade" }),
|
||||
content: varchar("content", { length: 500 }).notNull(),
|
||||
likesCount: integer("likes_count").notNull().default(0),
|
||||
|
|
@ -154,12 +154,12 @@ export const promoCode = pgTable('promo_code', {
|
|||
isActive: boolean('is_active').notNull().default(true),
|
||||
expiresAt: timestamp('expires_at', { withTimezone: true }),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
createdBy: integer('created_by').references(() => user.id),
|
||||
createdBy: integer('created_by').references(() => user.id, { onDelete: "set null" }),
|
||||
});
|
||||
|
||||
export const promoCodeRedemption = pgTable('promo_code_redemption', {
|
||||
id: serial('id').primaryKey(),
|
||||
userId: integer('user_id').notNull().references(() => user.id),
|
||||
userId: integer('user_id').references(() => user.id, { onDelete: "cascade" }),
|
||||
promoCodeId: integer('promo_code_id').notNull().references(() => promoCode.id),
|
||||
rewardAmount: decimal('reward_amount', { precision: 20, scale: 8 }).notNull(),
|
||||
redeemedAt: timestamp('redeemed_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
|
|
@ -169,7 +169,7 @@ export const promoCodeRedemption = pgTable('promo_code_redemption', {
|
|||
|
||||
export const predictionQuestion = pgTable("prediction_question", {
|
||||
id: serial("id").primaryKey(),
|
||||
creatorId: integer("creator_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
||||
creatorId: integer("creator_id").references(() => user.id, { onDelete: "set null" }),
|
||||
question: varchar("question", { length: 200 }).notNull(),
|
||||
status: predictionMarketEnum("status").notNull().default("ACTIVE"),
|
||||
resolutionDate: timestamp("resolution_date", { withTimezone: true }).notNull(),
|
||||
|
|
@ -192,7 +192,7 @@ export const predictionQuestion = pgTable("prediction_question", {
|
|||
|
||||
export const predictionBet = pgTable("prediction_bet", {
|
||||
id: serial("id").primaryKey(),
|
||||
userId: integer("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
||||
userId: integer("user_id").references(() => user.id, { onDelete: "set null" }),
|
||||
questionId: integer("question_id").notNull().references(() => predictionQuestion.id, { onDelete: "cascade" }),
|
||||
side: boolean("side").notNull(), // true = YES, false = NO
|
||||
amount: decimal("amount", { precision: 20, scale: 8 }).notNull(),
|
||||
|
|
@ -207,4 +207,21 @@ export const predictionBet = pgTable("prediction_bet", {
|
|||
createdAtIdx: index("prediction_bet_created_at_idx").on(table.createdAt),
|
||||
amountCheck: check("amount_positive", sql`amount > 0`),
|
||||
};
|
||||
});
|
||||
|
||||
export const accountDeletionRequest = pgTable("account_deletion_request", {
|
||||
id: serial("id").primaryKey(),
|
||||
userId: integer("user_id").notNull().references(() => user.id, { onDelete: "cascade" }).unique(),
|
||||
requestedAt: timestamp("requested_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
scheduledDeletionAt: timestamp("scheduled_deletion_at", { withTimezone: true }).notNull(),
|
||||
reason: text("reason"),
|
||||
isProcessed: boolean("is_processed").notNull().default(false),
|
||||
}, (table) => {
|
||||
return {
|
||||
userIdIdx: index("account_deletion_request_user_id_idx").on(table.userId),
|
||||
scheduledDeletionIdx: index("account_deletion_request_scheduled_deletion_idx").on(table.scheduledDeletionAt),
|
||||
oneOpenRequest: index("account_deletion_request_open_idx")
|
||||
.on(table.userId)
|
||||
.where(sql`is_processed = false`),
|
||||
};
|
||||
});
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { db } from '$lib/server/db';
|
||||
import { predictionQuestion, predictionBet, user } from '$lib/server/db/schema';
|
||||
import { predictionQuestion, predictionBet, user, accountDeletionRequest, session, account, promoCodeRedemption, userPortfolio, commentLike, comment, transaction, coin } from '$lib/server/db/schema';
|
||||
import { eq, and, lte, isNull } from 'drizzle-orm';
|
||||
import { resolveQuestion, getRugplayData } from '$lib/server/ai';
|
||||
|
||||
|
|
@ -83,7 +83,7 @@ export async function resolveExpiredQuestions() {
|
|||
})
|
||||
.where(eq(predictionBet.id, bet.id));
|
||||
|
||||
if (won && winnings > 0) {
|
||||
if (won && winnings > 0 && bet.userId !== null) {
|
||||
const [userData] = await tx
|
||||
.select({ baseCurrencyBalance: user.baseCurrencyBalance })
|
||||
.from(user)
|
||||
|
|
@ -113,4 +113,74 @@ export async function resolveExpiredQuestions() {
|
|||
} catch (error) {
|
||||
console.error('Error in resolveExpiredQuestions:', error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function processAccountDeletions() {
|
||||
const now = new Date();
|
||||
|
||||
try {
|
||||
const expiredRequests = await db.select()
|
||||
.from(accountDeletionRequest)
|
||||
.where(
|
||||
and(
|
||||
lte(accountDeletionRequest.scheduledDeletionAt, now),
|
||||
eq(accountDeletionRequest.isProcessed, false)
|
||||
)
|
||||
);
|
||||
|
||||
console.log(`🗑️ Processing ${expiredRequests.length} expired account deletion requests`);
|
||||
|
||||
for (const request of expiredRequests) {
|
||||
try {
|
||||
await db.transaction(async (tx) => {
|
||||
const userId = request.userId;
|
||||
|
||||
await tx.update(transaction)
|
||||
.set({ userId: null })
|
||||
.where(eq(transaction.userId, userId));
|
||||
|
||||
await tx.update(comment)
|
||||
.set({ userId: null, content: "[deleted]", isDeleted: true })
|
||||
.where(eq(comment.userId, userId));
|
||||
|
||||
await tx.update(predictionBet)
|
||||
.set({ userId: null })
|
||||
.where(eq(predictionBet.userId, userId));
|
||||
|
||||
await tx.update(predictionQuestion)
|
||||
.set({ creatorId: null })
|
||||
.where(eq(predictionQuestion.creatorId, userId));
|
||||
|
||||
await tx.update(coin)
|
||||
.set({ creatorId: null })
|
||||
.where(eq(coin.creatorId, userId));
|
||||
|
||||
await tx.delete(session).where(eq(session.userId, userId));
|
||||
await tx.delete(account).where(eq(account.userId, userId));
|
||||
await tx.delete(promoCodeRedemption).where(eq(promoCodeRedemption.userId, userId));
|
||||
await tx.delete(userPortfolio).where(eq(userPortfolio.userId, userId));
|
||||
await tx.delete(commentLike).where(eq(commentLike.userId, userId));
|
||||
|
||||
await tx.update(accountDeletionRequest)
|
||||
.set({ isProcessed: true })
|
||||
.where(eq(accountDeletionRequest.id, request.id));
|
||||
|
||||
await tx.delete(user).where(eq(user.id, userId));
|
||||
});
|
||||
|
||||
console.log(`✅ Successfully processed account deletion for user ID: ${request.userId}`);
|
||||
} catch (error: any) {
|
||||
console.error(`❌ Failed to process account deletion for user ID: ${request.userId}`, error);
|
||||
|
||||
await db.update(accountDeletionRequest)
|
||||
.set({
|
||||
isProcessed: true, // Mark as processed to avoid retries, but log the failure
|
||||
reason: request.reason ? `${request.reason} - FAILED: ${error.message}` : `FAILED: ${error.message}`
|
||||
})
|
||||
.where(eq(accountDeletionRequest.id, request.id));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error processing account deletions:', error);
|
||||
}
|
||||
}
|
||||
|
|
@ -26,16 +26,17 @@ export interface PredictionQuestion {
|
|||
estimatedYesWinnings?: number;
|
||||
estimatedNoWinnings?: number;
|
||||
};
|
||||
// fuck gdpr and all that fucking shit
|
||||
recentBets?: Array<{
|
||||
id: number;
|
||||
id?: number;
|
||||
side: boolean;
|
||||
amount: number;
|
||||
createdAt: string;
|
||||
user: {
|
||||
id: number;
|
||||
name: string;
|
||||
username: string;
|
||||
image: string;
|
||||
user?: {
|
||||
id?: number;
|
||||
name?: string;
|
||||
username?: string;
|
||||
image?: string;
|
||||
};
|
||||
}>;
|
||||
}
|
||||
|
|
|
|||
Reference in a new issue