mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
feat(licenses): enhance API and database integration for checkout sessions
- Updated development server port in package.json to 4002. - Introduced constants for website URLs based on environment. - Refactored database connection logic to use drizzle with PostgreSQL. - Added new API endpoint for creating checkout sessions with Stripe integration. - Implemented utility function to generate Stripe items based on license type and quantity. - Updated existing API routes to use a router for better organization.
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "PORT=4000 tsx watch src/index.ts",
|
"dev": "PORT=4002 tsx watch src/index.ts",
|
||||||
"build": "tsc --project tsconfig.json",
|
"build": "tsc --project tsconfig.json",
|
||||||
"start": "node dist/index.js",
|
"start": "node dist/index.js",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
|
|||||||
4
apps/licenses/src/constants.ts
Normal file
4
apps/licenses/src/constants.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export const WEBSITE_URL =
|
||||||
|
process.env.NODE_ENV === "development"
|
||||||
|
? "http://localhost:3001"
|
||||||
|
: process.env.SITE_URL;
|
||||||
@@ -1,9 +1,30 @@
|
|||||||
import { drizzle } from "drizzle-orm/node-postgres";
|
// import { drizzle } from "drizzle-orm/node-postgres";
|
||||||
import { Pool } from "pg";
|
// import { Pool } from "pg";
|
||||||
|
// import * as schema from "./schema";
|
||||||
|
|
||||||
|
// const pool = new Pool({
|
||||||
|
// connectionString: process.env.DATABASE_URL,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// export const db = drizzle(pool, { schema });
|
||||||
|
|
||||||
|
import { type PostgresJsDatabase, drizzle } from "drizzle-orm/postgres-js";
|
||||||
|
import postgres from "postgres";
|
||||||
import * as schema from "./schema";
|
import * as schema from "./schema";
|
||||||
|
declare global {
|
||||||
|
var db: PostgresJsDatabase<typeof schema> | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
const pool = new Pool({
|
export let db: PostgresJsDatabase<typeof schema>;
|
||||||
connectionString: process.env.DATABASE_URL,
|
if (process.env.NODE_ENV === "production") {
|
||||||
});
|
db = drizzle(postgres(process.env.DATABASE_URL!), {
|
||||||
|
schema,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (!global.db)
|
||||||
|
global.db = drizzle(postgres(process.env.DATABASE_URL!), {
|
||||||
|
schema,
|
||||||
|
});
|
||||||
|
|
||||||
export const db = drizzle(pool, { schema });
|
db = global.db;
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,13 +5,14 @@ import { z } from "zod";
|
|||||||
import { zValidator } from "@hono/zod-validator";
|
import { zValidator } from "@hono/zod-validator";
|
||||||
import { logger } from "./logger";
|
import { logger } from "./logger";
|
||||||
import { render } from "@react-email/render";
|
import { render } from "@react-email/render";
|
||||||
import { LicenseEmail } from "../templates/emails/license-email";
|
import LicenseEmail from "../templates/emails/license-email";
|
||||||
import { ResendLicenseEmail } from "../templates/emails/resend-license-email";
|
import ResendLicenseEmail from "../templates/emails/resend-license-email";
|
||||||
import {
|
import {
|
||||||
createLicense,
|
createLicense,
|
||||||
validateLicense,
|
validateLicense,
|
||||||
activateLicense,
|
activateLicense,
|
||||||
deactivateLicense,
|
deactivateLicense,
|
||||||
|
getStripeItems,
|
||||||
} from "./utils/license";
|
} from "./utils/license";
|
||||||
import { db } from "./db";
|
import { db } from "./db";
|
||||||
import { eq, sql } from "drizzle-orm";
|
import { eq, sql } from "drizzle-orm";
|
||||||
@@ -21,9 +22,17 @@ import { getLicenseFeatures, getLicenseTypeFromPriceId } from "./utils";
|
|||||||
import { transporter } from "./email";
|
import { transporter } from "./email";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import { stripe } from "./stripe";
|
import { stripe } from "./stripe";
|
||||||
|
import { WEBSITE_URL } from "./constants";
|
||||||
|
import { createCheckoutSessionSchema } from "./validators/stripe";
|
||||||
|
|
||||||
const app = new Hono();
|
const app = new Hono();
|
||||||
app.use("/*", cors());
|
const router = new Hono();
|
||||||
|
router.use(
|
||||||
|
"/*",
|
||||||
|
cors({
|
||||||
|
origin: ["http://localhost:3001"],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const validateSchema = z.object({
|
const validateSchema = z.object({
|
||||||
licenseKey: z.string(),
|
licenseKey: z.string(),
|
||||||
@@ -34,7 +43,7 @@ const resendSchema = z.object({
|
|||||||
licenseKey: z.string(),
|
licenseKey: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get("/health", async (c) => {
|
router.get("/health", async (c) => {
|
||||||
try {
|
try {
|
||||||
await db.execute(sql`SELECT 1`);
|
await db.execute(sql`SELECT 1`);
|
||||||
return c.json({ status: "ok" });
|
return c.json({ status: "ok" });
|
||||||
@@ -44,7 +53,7 @@ app.get("/health", async (c) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/validate", zValidator("json", validateSchema), async (c) => {
|
router.post("/validate", zValidator("json", validateSchema), async (c) => {
|
||||||
const { licenseKey, serverIp } = c.req.valid("json");
|
const { licenseKey, serverIp } = c.req.valid("json");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -56,7 +65,7 @@ app.post("/validate", zValidator("json", validateSchema), async (c) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/activate", zValidator("json", validateSchema), async (c) => {
|
router.post("/activate", zValidator("json", validateSchema), async (c) => {
|
||||||
const { licenseKey, serverIp } = c.req.valid("json");
|
const { licenseKey, serverIp } = c.req.valid("json");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -71,7 +80,26 @@ app.post("/activate", zValidator("json", validateSchema), async (c) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/resend-license", zValidator("json", resendSchema), async (c) => {
|
router.post(
|
||||||
|
"/create-checkout-session",
|
||||||
|
zValidator("json", createCheckoutSessionSchema),
|
||||||
|
async (c) => {
|
||||||
|
const { type, serverQuantity, isAnnual } = c.req.valid("json");
|
||||||
|
|
||||||
|
const items = getStripeItems(type, serverQuantity, isAnnual);
|
||||||
|
const session = await stripe.checkout.sessions.create({
|
||||||
|
mode: "subscription",
|
||||||
|
line_items: items,
|
||||||
|
allow_promotion_codes: true,
|
||||||
|
success_url: `${WEBSITE_URL}/license/success`,
|
||||||
|
cancel_url: `${WEBSITE_URL}#pricing`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return c.json({ sessionId: session.id });
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
router.post("/resend-license", zValidator("json", resendSchema), async (c) => {
|
||||||
const { licenseKey } = c.req.valid("json");
|
const { licenseKey } = c.req.valid("json");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -107,7 +135,7 @@ app.post("/resend-license", zValidator("json", resendSchema), async (c) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/stripe/webhook", async (c) => {
|
router.post("/stripe/webhook", async (c) => {
|
||||||
const sig = c.req.header("stripe-signature");
|
const sig = c.req.header("stripe-signature");
|
||||||
const body = await c.req.json();
|
const body = await c.req.json();
|
||||||
|
|
||||||
@@ -289,7 +317,8 @@ app.post("/stripe/webhook", async (c) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const port = process.env.PORT || 4000;
|
app.route("/api", router);
|
||||||
|
const port = process.env.PORT || 4002;
|
||||||
console.log(`Server is running on port ${port}`);
|
console.log(`Server is running on port ${port}`);
|
||||||
|
|
||||||
serve({
|
serve({
|
||||||
|
|||||||
@@ -158,3 +158,38 @@ export const getLicenseStatus = (license: License) => {
|
|||||||
return "pending payment";
|
return "pending payment";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getStripeItems = (
|
||||||
|
type: "basic" | "premium" | "business",
|
||||||
|
serverQuantity: number,
|
||||||
|
isAnnual: boolean,
|
||||||
|
) => {
|
||||||
|
const items = [];
|
||||||
|
|
||||||
|
if (type === "basic") {
|
||||||
|
items.push({
|
||||||
|
price: isAnnual
|
||||||
|
? process.env.SELF_HOSTED_BASIC_PRICE_ANNUAL_ID
|
||||||
|
: process.env.SELF_HOSTED_BASIC_PRICE_MONTHLY_ID,
|
||||||
|
quantity: serverQuantity,
|
||||||
|
});
|
||||||
|
} else if (type === "premium") {
|
||||||
|
items.push({
|
||||||
|
price: isAnnual
|
||||||
|
? process.env.SELF_HOSTED_PREMIUM_PRICE_ANNUAL_ID
|
||||||
|
: process.env.SELF_HOSTED_PREMIUM_PRICE_MONTHLY_ID,
|
||||||
|
quantity: serverQuantity,
|
||||||
|
});
|
||||||
|
} else if (type === "business") {
|
||||||
|
items.push({
|
||||||
|
price: isAnnual
|
||||||
|
? process.env.SELF_HOSTED_BUSINESS_PRICE_ANNUAL_ID
|
||||||
|
: process.env.SELF_HOSTED_BUSINESS_PRICE_MONTHLY_ID,
|
||||||
|
quantity: serverQuantity,
|
||||||
|
});
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
};
|
||||||
|
|||||||
7
apps/licenses/src/validators/stripe.ts
Normal file
7
apps/licenses/src/validators/stripe.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const createCheckoutSessionSchema = z.object({
|
||||||
|
type: z.enum(["basic", "premium", "business"]),
|
||||||
|
serverQuantity: z.number().min(1),
|
||||||
|
isAnnual: z.boolean(),
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user