mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-19 06:05:25 +02: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",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "PORT=4000 tsx watch src/index.ts",
|
||||
"dev": "PORT=4002 tsx watch src/index.ts",
|
||||
"build": "tsc --project tsconfig.json",
|
||||
"start": "node dist/index.js",
|
||||
"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 { Pool } from "pg";
|
||||
// import { drizzle } from "drizzle-orm/node-postgres";
|
||||
// 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";
|
||||
declare global {
|
||||
var db: PostgresJsDatabase<typeof schema> | undefined;
|
||||
}
|
||||
|
||||
const pool = new Pool({
|
||||
connectionString: process.env.DATABASE_URL,
|
||||
});
|
||||
export let db: PostgresJsDatabase<typeof schema>;
|
||||
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 { logger } from "./logger";
|
||||
import { render } from "@react-email/render";
|
||||
import { LicenseEmail } from "../templates/emails/license-email";
|
||||
import { ResendLicenseEmail } from "../templates/emails/resend-license-email";
|
||||
import LicenseEmail from "../templates/emails/license-email";
|
||||
import ResendLicenseEmail from "../templates/emails/resend-license-email";
|
||||
import {
|
||||
createLicense,
|
||||
validateLicense,
|
||||
activateLicense,
|
||||
deactivateLicense,
|
||||
getStripeItems,
|
||||
} from "./utils/license";
|
||||
import { db } from "./db";
|
||||
import { eq, sql } from "drizzle-orm";
|
||||
@@ -21,9 +22,17 @@ import { getLicenseFeatures, getLicenseTypeFromPriceId } from "./utils";
|
||||
import { transporter } from "./email";
|
||||
import type Stripe from "stripe";
|
||||
import { stripe } from "./stripe";
|
||||
import { WEBSITE_URL } from "./constants";
|
||||
import { createCheckoutSessionSchema } from "./validators/stripe";
|
||||
|
||||
const app = new Hono();
|
||||
app.use("/*", cors());
|
||||
const router = new Hono();
|
||||
router.use(
|
||||
"/*",
|
||||
cors({
|
||||
origin: ["http://localhost:3001"],
|
||||
}),
|
||||
);
|
||||
|
||||
const validateSchema = z.object({
|
||||
licenseKey: z.string(),
|
||||
@@ -34,7 +43,7 @@ const resendSchema = z.object({
|
||||
licenseKey: z.string(),
|
||||
});
|
||||
|
||||
app.get("/health", async (c) => {
|
||||
router.get("/health", async (c) => {
|
||||
try {
|
||||
await db.execute(sql`SELECT 1`);
|
||||
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");
|
||||
|
||||
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");
|
||||
|
||||
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");
|
||||
|
||||
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 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}`);
|
||||
|
||||
serve({
|
||||
|
||||
@@ -158,3 +158,38 @@ export const getLicenseStatus = (license: License) => {
|
||||
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