Skip to content

Commit fab3a56

Browse files
Merge pull request #655 from sparrowapp-dev/development
patch: Patch release for Admin Panel
2 parents 165b68b + f49c8d8 commit fab3a56

34 files changed

+1751
-293
lines changed

.env.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,5 @@ STRIPE_PUBLISHABLE_KEY= # Stripe publishable key (frontend)
118118
STRIPE_SECRET_KEY= # Stripe secret key (backend - KEEP SECURE!)
119119

120120
# [Sparrow Admin]
121-
SPARROW_ADMIN_KEY= # Sparrow admin key for privileged operations specific to Sparrow team
121+
SPARROW_ADMIN_KEY= # Sparrow admin key for privileged operations specific to Sparrow team
122+
ADMIN_BASE_URL= # Base URL for Sparrow admin operations

deploymentManifests/deployment.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,11 @@ spec:
371371
secretKeyRef:
372372
name: sparrow-api-secret
373373
key: SPARROW_ADMIN_KEY
374+
- name: ADMIN_BASE_URL
375+
valueFrom:
376+
secretKeyRef:
377+
name: sparrow-api-secret
378+
key: ADMIN_BASE_URL
374379

375380
---
376381
apiVersion: v1

deploymentManifests/release-v2.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,11 @@ spec:
340340
secretKeyRef:
341341
name: release-api-v2-secret
342342
key: SPARROW_ADMIN_KEY
343+
- name: ADMIN_BASE_URL
344+
valueFrom:
345+
secretKeyRef:
346+
name: release-api-v2-secret
347+
key: ADMIN_BASE_URL
343348

344349
---
345350
apiVersion: v1
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { Injectable, OnModuleInit, Inject } from "@nestjs/common";
2+
import { Collections } from "@src/modules/common/enum/database.collection.enum";
3+
import { Db, ObjectId } from "mongodb";
4+
5+
@Injectable()
6+
export class UpdateHubBillingMigration implements OnModuleInit {
7+
private hasRun = false;
8+
9+
constructor(@Inject("DATABASE_CONNECTION") private readonly db: Db) {}
10+
11+
async onModuleInit(): Promise<void> {
12+
if (this.hasRun) return;
13+
14+
try {
15+
console.log(
16+
"\x1b[36m[Nest]\x1b[0m \x1b[36mStarting Updating Billing...\x1b[0m",
17+
);
18+
// Add pricing details to pricing collection
19+
// const pricing = {
20+
// name: "Default Pricing",
21+
// currency: "usd",
22+
// plans: [
23+
// {
24+
// tier: "Standard",
25+
// plan_name: "Standard",
26+
// billing: [
27+
// {
28+
// interval: "monthly",
29+
// price: 9.99,
30+
// providers: {
31+
// stripe: "id1",
32+
// },
33+
// },
34+
// {
35+
// interval: "annual",
36+
// price: 99,
37+
// providers: {
38+
// stripe: "id2",
39+
// },
40+
// },
41+
// ],
42+
// },
43+
// {
44+
// tier: "Professional",
45+
// plan_name: "Professional",
46+
// billing: [
47+
// {
48+
// interval: "monthly",
49+
// price: 19.99,
50+
// providers: {
51+
// stripe: "id3",
52+
// },
53+
// },
54+
// {
55+
// interval: "annual",
56+
// price: 199,
57+
// providers: {
58+
// stripe: "oid4",
59+
// },
60+
// },
61+
// ],
62+
// },
63+
// ],
64+
// version: 1,
65+
// created_at: "2025-07-09T11:00:00.000Z",
66+
// };
67+
68+
// await this.addPricing(pricing);
69+
const teamId = "team213123123";
70+
const billing = {
71+
current_period_start: new Date("2025-07-08T13:04:24.000Z"),
72+
current_period_end: new Date("2025-08-08T13:04:24.000Z"),
73+
amount_billed: 119.88,
74+
currency: "usd",
75+
status: "active",
76+
collection_method: "charge_manually",
77+
paid_at: new Date("2025-07-08T13:04:28.000Z"),
78+
billingType: "paid",
79+
updatedBy: "system-admin",
80+
in_trial: false,
81+
paymentProviders: [
82+
{
83+
id: "37069b1a-b266-4e9c-a010-31cd30075ac4",
84+
provider: "stripe",
85+
currentPaymentMethod: true,
86+
subscriptionId: "",
87+
payment_method: ["card"],
88+
customerId: "cus_SdRmVfW1qK0Ruw",
89+
updatedAt: new Date("2025-07-08T13:04:29.146Z"),
90+
},
91+
],
92+
};
93+
await this.addBillingToTeam(teamId, billing);
94+
95+
this.hasRun = true;
96+
} catch (error) {
97+
console.error(
98+
"\x1b[31m[Nest] Error during UpdateHubBillingMigration:\x1b[0m",
99+
error,
100+
);
101+
}
102+
}
103+
private async addPricing(pricing: any): Promise<void> {
104+
const pricingCollection = this.db.collection(Collections.PRICING);
105+
await pricingCollection.insertOne(pricing);
106+
console.log(
107+
"\x1b[32m[Nest]\x1b[0m Pricing details added to pricing collection",
108+
);
109+
}
110+
private async addBillingToTeam(teamId: string, billing: any): Promise<void> {
111+
const teamCollection = this.db.collection(Collections.TEAM);
112+
await teamCollection.updateOne(
113+
{ _id: new ObjectId(teamId) },
114+
{
115+
$set: {
116+
billing: billing,
117+
},
118+
},
119+
);
120+
console.log(`\x1b[32m[Nest]\x1b[0m Billing info added to team ${teamId}`);
121+
}
122+
}

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
"start:migration-chatbot-ai-model": "ts-node -r tsconfig-paths/register src/migration-chatbotstats.ts",
3232
"start:create-plan": "ts-node -r tsconfig-paths/register src/create-plan.ts",
3333
"start:upgrade-plan": "ts-node -r tsconfig-paths/register src/upgrade-plan.ts",
34-
"start:migration-mock-url": "ts-node -r tsconfig-paths/register src/migrationMockRequestUrl.ts"
34+
"start:migration-mock-url": "ts-node -r tsconfig-paths/register src/migrationMockRequestUrl.ts",
35+
"start:migration-hub-billing": "ts-node -r tsconfig-paths/register src/migrationHubBilling.ts"
3536
},
3637
"dependencies": {
3738
"@anthropic-ai/sdk": "^0.54.0",
@@ -141,4 +142,4 @@
141142
"optionalDependencies": {
142143
"@sparrowapp-dev/stripe-billing": "^1.1.1"
143144
}
144-
}
145+
}

src/migrationHubBilling.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { NestFactory } from "@nestjs/core";
2+
import { Module, Provider } from "@nestjs/common";
3+
import { MongoClient, Db } from "mongodb";
4+
import { ConfigModule, ConfigService } from "@nestjs/config";
5+
import configuration from "./modules/common/config/configuration";
6+
import { UpdateHubBillingMigration } from "migrations/update-hub-billing.migration";
7+
8+
const databaseProvider: Provider = {
9+
provide: "DATABASE_CONNECTION",
10+
useFactory: async (configService: ConfigService): Promise<Db> => {
11+
const dbUrl = configService.get<string>("db.url");
12+
13+
if (!dbUrl) {
14+
throw new Error("Database URL is not defined in the configuration.");
15+
}
16+
17+
const client = new MongoClient(dbUrl);
18+
await client.connect();
19+
return client.db("sparrow");
20+
},
21+
inject: [ConfigService],
22+
};
23+
24+
@Module({
25+
imports: [
26+
ConfigModule.forRoot({
27+
isGlobal: true,
28+
load: [configuration],
29+
}),
30+
],
31+
providers: [databaseProvider, UpdateHubBillingMigration],
32+
})
33+
class MigrationModule {}
34+
35+
async function run() {
36+
const app = await NestFactory.createApplicationContext(MigrationModule);
37+
const migration = app.get(UpdateHubBillingMigration);
38+
await migration.onModuleInit();
39+
await app.close();
40+
}
41+
42+
run();

src/modules/common/config/configuration.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,7 @@ export default () => ({
127127
sparrowAdmin: {
128128
adminKey: process.env.SPARROW_ADMIN_KEY,
129129
},
130+
admin: {
131+
baseURL: process.env.ADMIN_BASE_URL,
132+
},
130133
});

src/modules/common/enum/database.collection.enum.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ export enum Collections {
1717
USERLIMITLOGS = "userlimitlogs",
1818
LLMCONVERSATION = "llmconversation",
1919
SALESEMAIL = "salesemail",
20+
PRICING = "pricing",
2021
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { Type } from "class-transformer";
2+
import {
3+
IsArray,
4+
IsDateString,
5+
IsNotEmpty,
6+
IsNumber,
7+
IsObject,
8+
IsOptional,
9+
IsString,
10+
ValidateNested,
11+
} from "class-validator";
12+
13+
// Providers for a billing option (e.g., Stripe, PayPal, etc.)
14+
export class PricingPlanProviders {
15+
@IsString()
16+
@IsOptional()
17+
stripe?: string;
18+
}
19+
20+
// Billing option for a plan (monthly, annual, etc.)
21+
export class PricingPlanBilling {
22+
@IsString()
23+
@IsNotEmpty()
24+
interval: string;
25+
26+
@IsNumber()
27+
@IsNotEmpty()
28+
price: number;
29+
30+
@IsObject()
31+
@ValidateNested()
32+
@Type(() => PricingPlanProviders)
33+
providers: PricingPlanProviders;
34+
}
35+
36+
// A single plan (Standard, Professional, etc.)
37+
export class PricingPlan {
38+
@IsString()
39+
@IsNotEmpty()
40+
tier: string;
41+
42+
@IsString()
43+
@IsNotEmpty()
44+
plan_name: string;
45+
46+
@IsArray()
47+
@ValidateNested({ each: true })
48+
@Type(() => PricingPlanBilling)
49+
billing: PricingPlanBilling[];
50+
}
51+
52+
// The main pricing model
53+
export class Pricing {
54+
@IsString()
55+
@IsNotEmpty()
56+
name: string;
57+
58+
@IsString()
59+
@IsNotEmpty()
60+
currency: string;
61+
62+
@IsArray()
63+
@ValidateNested({ each: true })
64+
@Type(() => PricingPlan)
65+
plans: PricingPlan[];
66+
67+
@IsNumber()
68+
@IsNotEmpty()
69+
version: number;
70+
71+
@IsDateString()
72+
@IsNotEmpty()
73+
created_at: string;
74+
}

src/modules/common/models/sales-email.model.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ export class SalesEmail {
2121
@IsNotEmpty()
2222
isHubCreated: boolean;
2323

24+
@IsString()
25+
@IsOptional()
26+
createdHubId?: string;
27+
2428
@IsString()
2529
@IsOptional()
2630
companyName?: string;

0 commit comments

Comments
 (0)