Skip to content

Commit

Permalink
feat(user): implement stale anonymous users cleanup cli command
Browse files Browse the repository at this point in the history
refs #464
  • Loading branch information
ygrishajev committed Nov 14, 2024
1 parent 17f0ad3 commit 6e7505e
Show file tree
Hide file tree
Showing 25 changed files with 617 additions and 35 deletions.
2 changes: 2 additions & 0 deletions apps/api/drizzle.config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import "@akashnetwork/env-loader";

import { defineConfig } from "drizzle-kit";

import { config } from "./src/core/config";
Expand Down
8 changes: 8 additions & 0 deletions apps/api/drizzle/0006_skinny_stingray.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
ALTER TABLE "user_wallets" DROP CONSTRAINT "user_wallets_user_id_userSetting_id_fk";
--> statement-breakpoint
ALTER TABLE "userSetting" ADD COLUMN "last_active_at" timestamp DEFAULT now();--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "user_wallets" ADD CONSTRAINT "user_wallets_user_id_userSetting_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."userSetting"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
286 changes: 286 additions & 0 deletions apps/api/drizzle/meta/0006_snapshot.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
{
"id": "d83b4940-34c1-400c-98d9-5a1eb935fe5e",
"prevId": "d6102ad7-0e0c-4ef8-8712-6a626f5ad2a1",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.user_wallets": {
"name": "user_wallets",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"address": {
"name": "address",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"stripe_customer_id": {
"name": "stripe_customer_id",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"deployment_allowance": {
"name": "deployment_allowance",
"type": "numeric(20, 2)",
"primaryKey": false,
"notNull": true,
"default": "'0.00'"
},
"fee_allowance": {
"name": "fee_allowance",
"type": "numeric(20, 2)",
"primaryKey": false,
"notNull": true,
"default": "'0.00'"
},
"trial": {
"name": "trial",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"user_wallets_user_id_userSetting_id_fk": {
"name": "user_wallets_user_id_userSetting_id_fk",
"tableFrom": "user_wallets",
"tableTo": "userSetting",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"user_wallets_user_id_unique": {
"name": "user_wallets_user_id_unique",
"nullsNotDistinct": false,
"columns": [
"user_id"
]
},
"user_wallets_address_unique": {
"name": "user_wallets_address_unique",
"nullsNotDistinct": false,
"columns": [
"address"
]
}
}
},
"public.checkout_sessions": {
"name": "checkout_sessions",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "uuid_generate_v4()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"session_id": {
"name": "session_id",
"type": "varchar",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"checkout_sessions_user_id_userSetting_id_fk": {
"name": "checkout_sessions_user_id_userSetting_id_fk",
"tableFrom": "checkout_sessions",
"tableTo": "userSetting",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"checkout_sessions_session_id_unique": {
"name": "checkout_sessions_session_id_unique",
"nullsNotDistinct": false,
"columns": [
"session_id"
]
}
}
},
"public.userSetting": {
"name": "userSetting",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "uuid_generate_v4()"
},
"userId": {
"name": "userId",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"username": {
"name": "username",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"emailVerified": {
"name": "emailVerified",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"default": false
},
"stripeCustomerId": {
"name": "stripeCustomerId",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"bio": {
"name": "bio",
"type": "text",
"primaryKey": false,
"notNull": false
},
"subscribedToNewsletter": {
"name": "subscribedToNewsletter",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"default": false
},
"youtubeUsername": {
"name": "youtubeUsername",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"twitterUsername": {
"name": "twitterUsername",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"githubUsername": {
"name": "githubUsername",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"last_active_at": {
"name": "last_active_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"userSetting_userId_unique": {
"name": "userSetting_userId_unique",
"nullsNotDistinct": false,
"columns": [
"userId"
]
},
"userSetting_username_unique": {
"name": "userSetting_username_unique",
"nullsNotDistinct": false,
"columns": [
"username"
]
}
}
}
},
"enums": {},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}
7 changes: 7 additions & 0 deletions apps/api/drizzle/meta/_journal.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@
"when": 1724745930642,
"tag": "0005_colorful_dreaming_celestial",
"breakpoints": true
},
{
"idx": 6,
"version": "7",
"when": 1731579448104,
"tag": "0006_skinny_stingray",
"breakpoints": true
}
]
}
22 changes: 13 additions & 9 deletions apps/api/src/auth/services/auth.interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { AuthService } from "@src/auth/services/auth.service";
import { AuthTokenService } from "@src/auth/services/auth-token/auth-token.service";
import type { HonoInterceptor } from "@src/core/types/hono-interceptor.type";
import { kvStore } from "@src/middlewares/userMiddleware";
import { UserRepository } from "@src/user/repositories";
import { UserOutput, UserRepository } from "@src/user/repositories";
import { env } from "@src/utils/env";
import { getJwks, useKVStore, verify } from "@src/verify-rsa-jwt-cloudflare-worker-main";

Expand All @@ -27,21 +27,15 @@ export class AuthInterceptor implements HonoInterceptor {

if (anonymousUserId) {
const currentUser = await this.userRepository.findAnonymousById(anonymousUserId);

this.authService.currentUser = currentUser;
this.authService.ability = currentUser ? this.abilityService.getAbilityFor("REGULAR_ANONYMOUS_USER", currentUser) : this.abilityService.EMPTY_ABILITY;

await this.auth(currentUser);
return await next();
}

const userId = bearer && (await this.getValidUserId(bearer, c));

if (userId) {
const currentUser = await this.userRepository.findByUserId(userId);

this.authService.currentUser = currentUser;
this.authService.ability = currentUser ? this.abilityService.getAbilityFor("REGULAR_USER", currentUser) : this.abilityService.EMPTY_ABILITY;

this.auth(currentUser);
return await next();
}

Expand All @@ -51,6 +45,16 @@ export class AuthInterceptor implements HonoInterceptor {
};
}

private async auth(user?: UserOutput) {
this.authService.currentUser = user;
if (user) {
this.authService.ability = this.abilityService.getAbilityFor(user.userId ? "REGULAR_ANONYMOUS_USER" : "REGULAR_USER", user);
await this.userRepository.markAsActive(user.id);
} else {
this.authService.ability = this.abilityService.EMPTY_ABILITY;
}
}

private async getValidUserId(bearer: string, c: Context) {
const token = bearer.replace(/^Bearer\s+/i, "");
const jwks = await getJwks(env.AUTH0_JWKS_URI || c.env?.JWKS_URI, useKVStore(kvStore || c.env?.VERIFY_RSA_JWT), c.env?.VERIFY_RSA_JWT_JWKS_CACHE_KEY);
Expand Down
2 changes: 1 addition & 1 deletion apps/api/src/billing/config/env.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { z } from "zod";

const envSchema = z.object({
export const envSchema = z.object({
MASTER_WALLET_MNEMONIC: z.string(),
UAKT_TOP_UP_MASTER_WALLET_MNEMONIC: z.string(),
USDC_TOP_UP_MASTER_WALLET_MNEMONIC: z.string(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Users } from "@src/user/model-schemas";
export const UserWallets = pgTable("user_wallets", {
id: serial("id").primaryKey(),
userId: uuid("user_id")
.references(() => Users.id)
.references(() => Users.id, { onDelete: "cascade" })
.unique(),
address: varchar("address").unique(),
stripeCustomerId: varchar("stripe_customer_id"),
Expand Down
2 changes: 2 additions & 0 deletions apps/api/src/billing/providers/wallet.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ export const USDC_TOP_UP_MASTER_WALLET = "USDC_TOP_UP_MASTER_WALLET";
container.register(USDC_TOP_UP_MASTER_WALLET, { useFactory: () => new MasterWalletService(config.USDC_TOP_UP_MASTER_WALLET_MNEMONIC) });

export const InjectWallet = (walletType: MasterWalletType) => inject(`${walletType}_MASTER_WALLET`);

export const resolveWallet = (walletType: MasterWalletType) => container.resolve<MasterWalletService>(`${walletType}_MASTER_WALLET`);
Loading

0 comments on commit 6e7505e

Please sign in to comment.