Merge branch 'main' into dev

This commit is contained in:
Owen
2026-02-11 17:22:19 -08:00
13 changed files with 106 additions and 35 deletions

View File

@@ -56,22 +56,22 @@ export function getFeatureIdByMetricId(
export type FeaturePriceSet = Partial<Record<FeatureId, string>>; export type FeaturePriceSet = Partial<Record<FeatureId, string>>;
export const homeLabFeaturePriceSet: FeaturePriceSet = { export const tier1FeaturePriceSet: FeaturePriceSet = {
[FeatureId.TIER1]: "price_1SzVE3D3Ee2Ir7Wm6wT5Dl3G" [FeatureId.TIER1]: "price_1SzVE3D3Ee2Ir7Wm6wT5Dl3G"
}; };
export const homeLabFeaturePriceSetSandbox: FeaturePriceSet = { export const tier1FeaturePriceSetSandbox: FeaturePriceSet = {
[FeatureId.TIER1]: "price_1SxgpPDCpkOb237Bfo4rIsoT" [FeatureId.TIER1]: "price_1SxgpPDCpkOb237Bfo4rIsoT"
}; };
export function getHomeLabFeaturePriceSet(): FeaturePriceSet { export function getTier1FeaturePriceSet(): FeaturePriceSet {
if ( if (
process.env.ENVIRONMENT == "prod" && process.env.ENVIRONMENT == "prod" &&
process.env.SANDBOX_MODE !== "true" process.env.SANDBOX_MODE !== "true"
) { ) {
return homeLabFeaturePriceSet; return tier1FeaturePriceSet;
} else { } else {
return homeLabFeaturePriceSetSandbox; return tier1FeaturePriceSetSandbox;
} }
} }
@@ -83,7 +83,7 @@ export const tier2FeaturePriceSetSandbox: FeaturePriceSet = {
[FeatureId.USERS]: "price_1SxaEHDCpkOb237BD9lBkPiR" [FeatureId.USERS]: "price_1SxaEHDCpkOb237BD9lBkPiR"
}; };
export function getStarterFeaturePriceSet(): FeaturePriceSet { export function getTier2FeaturePriceSet(): FeaturePriceSet {
if ( if (
process.env.ENVIRONMENT == "prod" && process.env.ENVIRONMENT == "prod" &&
process.env.SANDBOX_MODE !== "true" process.env.SANDBOX_MODE !== "true"
@@ -102,7 +102,7 @@ export const tier3FeaturePriceSetSandbox: FeaturePriceSet = {
[FeatureId.USERS]: "price_1SxaEODCpkOb237BiXdCBSfs" [FeatureId.USERS]: "price_1SxaEODCpkOb237BiXdCBSfs"
}; };
export function getScaleFeaturePriceSet(): FeaturePriceSet { export function getTier3FeaturePriceSet(): FeaturePriceSet {
if ( if (
process.env.ENVIRONMENT == "prod" && process.env.ENVIRONMENT == "prod" &&
process.env.SANDBOX_MODE !== "true" process.env.SANDBOX_MODE !== "true"
@@ -116,9 +116,9 @@ export function getScaleFeaturePriceSet(): FeaturePriceSet {
export function getFeatureIdByPriceId(priceId: string): FeatureId | undefined { export function getFeatureIdByPriceId(priceId: string): FeatureId | undefined {
// Check all feature price sets // Check all feature price sets
const allPriceSets = [ const allPriceSets = [
getHomeLabFeaturePriceSet(), getTier1FeaturePriceSet(),
getStarterFeaturePriceSet(), getTier2FeaturePriceSet(),
getScaleFeaturePriceSet() getTier3FeaturePriceSet()
]; ];
for (const priceSet of allPriceSets) { for (const priceSet of allPriceSets) {

View File

@@ -22,9 +22,9 @@ import logger from "@server/logger";
import { fromError } from "zod-validation-error"; import { fromError } from "zod-validation-error";
import stripe from "#private/lib/stripe"; import stripe from "#private/lib/stripe";
import { import {
getHomeLabFeaturePriceSet, getTier1FeaturePriceSet,
getScaleFeaturePriceSet, getTier3FeaturePriceSet,
getStarterFeaturePriceSet, getTier2FeaturePriceSet,
FeatureId, FeatureId,
type FeaturePriceSet type FeaturePriceSet
} from "@server/lib/billing"; } from "@server/lib/billing";
@@ -113,11 +113,11 @@ export async function changeTier(
// Get the target tier's price set // Get the target tier's price set
let targetPriceSet: FeaturePriceSet; let targetPriceSet: FeaturePriceSet;
if (tier === "tier1") { if (tier === "tier1") {
targetPriceSet = getHomeLabFeaturePriceSet(); targetPriceSet = getTier1FeaturePriceSet();
} else if (tier === "tier2") { } else if (tier === "tier2") {
targetPriceSet = getStarterFeaturePriceSet(); targetPriceSet = getTier2FeaturePriceSet();
} else if (tier === "tier3") { } else if (tier === "tier3") {
targetPriceSet = getScaleFeaturePriceSet(); targetPriceSet = getTier3FeaturePriceSet();
} else { } else {
return next(createHttpError(HttpCode.BAD_REQUEST, "Invalid tier")); return next(createHttpError(HttpCode.BAD_REQUEST, "Invalid tier"));
} }

View File

@@ -23,9 +23,9 @@ import config from "@server/lib/config";
import { fromError } from "zod-validation-error"; import { fromError } from "zod-validation-error";
import stripe from "#private/lib/stripe"; import stripe from "#private/lib/stripe";
import { import {
getHomeLabFeaturePriceSet, getTier1FeaturePriceSet,
getScaleFeaturePriceSet, getTier3FeaturePriceSet,
getStarterFeaturePriceSet getTier2FeaturePriceSet
} from "@server/lib/billing"; } from "@server/lib/billing";
import { getLineItems } from "@server/lib/billing/getLineItems"; import { getLineItems } from "@server/lib/billing/getLineItems";
import Stripe from "stripe"; import Stripe from "stripe";
@@ -88,11 +88,11 @@ export async function createCheckoutSession(
let lineItems: Stripe.Checkout.SessionCreateParams.LineItem[]; let lineItems: Stripe.Checkout.SessionCreateParams.LineItem[];
if (tier === "tier1") { if (tier === "tier1") {
lineItems = await getLineItems(getHomeLabFeaturePriceSet(), orgId); lineItems = await getLineItems(getTier1FeaturePriceSet(), orgId);
} else if (tier === "tier2") { } else if (tier === "tier2") {
lineItems = await getLineItems(getStarterFeaturePriceSet(), orgId); lineItems = await getLineItems(getTier2FeaturePriceSet(), orgId);
} else if (tier === "tier3") { } else if (tier === "tier3") {
lineItems = await getLineItems(getScaleFeaturePriceSet(), orgId); lineItems = await getLineItems(getTier3FeaturePriceSet(), orgId);
} else { } else {
return next(createHttpError(HttpCode.BAD_REQUEST, "Invalid plan")); return next(createHttpError(HttpCode.BAD_REQUEST, "Invalid plan"));
} }

View File

@@ -15,9 +15,9 @@ import {
getLicensePriceSet, getLicensePriceSet,
} from "@server/lib/billing/licenses"; } from "@server/lib/billing/licenses";
import { import {
getHomeLabFeaturePriceSet, getTier1FeaturePriceSet,
getStarterFeaturePriceSet, getTier2FeaturePriceSet,
getScaleFeaturePriceSet, getTier3FeaturePriceSet,
} from "@server/lib/billing/features"; } from "@server/lib/billing/features";
import Stripe from "stripe"; import Stripe from "stripe";
import { Tier } from "@server/types/Tiers"; import { Tier } from "@server/types/Tiers";
@@ -40,19 +40,19 @@ export function getSubType(fullSubscription: Stripe.Response<Stripe.Subscription
} }
// Check if price ID matches home lab tier // Check if price ID matches home lab tier
const homeLabPrices = Object.values(getHomeLabFeaturePriceSet()); const homeLabPrices = Object.values(getTier1FeaturePriceSet());
if (homeLabPrices.includes(priceId)) { if (homeLabPrices.includes(priceId)) {
return "tier1"; return "tier1";
} }
// Check if price ID matches tier2 tier // Check if price ID matches tier2 tier
const tier2Prices = Object.values(getStarterFeaturePriceSet()); const tier2Prices = Object.values(getTier2FeaturePriceSet());
if (tier2Prices.includes(priceId)) { if (tier2Prices.includes(priceId)) {
return "tier2"; return "tier2";
} }
// Check if price ID matches tier3 tier // Check if price ID matches tier3 tier
const tier3Prices = Object.values(getScaleFeaturePriceSet()); const tier3Prices = Object.values(getTier3FeaturePriceSet());
if (tier3Prices.includes(priceId)) { if (tier3Prices.includes(priceId)) {
return "tier3"; return "tier3";
} }

View File

@@ -25,7 +25,7 @@ import { generateOidcRedirectUrl } from "@server/lib/idp/generateRedirectUrl";
import { encrypt } from "@server/lib/crypto"; import { encrypt } from "@server/lib/crypto";
import config from "@server/lib/config"; import config from "@server/lib/config";
import { CreateOrgIdpResponse } from "@server/routers/orgIdp/types"; import { CreateOrgIdpResponse } from "@server/routers/orgIdp/types";
import { isSubscribed } from "#dynamic/lib/isSubscribed"; import { isSubscribed } from "#private/lib/isSubscribed";
import { tierMatrix } from "@server/lib/billing/tierMatrix"; import { tierMatrix } from "@server/lib/billing/tierMatrix";
const paramsSchema = z.strictObject({ orgId: z.string().nonempty() }); const paramsSchema = z.strictObject({ orgId: z.string().nonempty() });

View File

@@ -24,7 +24,7 @@ import { idp, idpOidcConfig } from "@server/db";
import { eq, and } from "drizzle-orm"; import { eq, and } from "drizzle-orm";
import { encrypt } from "@server/lib/crypto"; import { encrypt } from "@server/lib/crypto";
import config from "@server/lib/config"; import config from "@server/lib/config";
import { isSubscribed } from "#dynamic/lib/isSubscribed"; import { isSubscribed } from "#private/lib/isSubscribed";
import { tierMatrix } from "@server/lib/billing/tierMatrix"; import { tierMatrix } from "@server/lib/billing/tierMatrix";
const paramsSchema = z const paramsSchema = z

View File

@@ -39,7 +39,7 @@ import {
import { logRequestAudit } from "./logRequestAudit"; import { logRequestAudit } from "./logRequestAudit";
import cache from "@server/lib/cache"; import cache from "@server/lib/cache";
import { APP_VERSION } from "@server/lib/consts"; import { APP_VERSION } from "@server/lib/consts";
import { isSubscribed } from "#private/lib/isSubscribed"; import { isSubscribed } from "#dynamic/lib/isSubscribed";
import { tierMatrix } from "@server/lib/billing/tierMatrix"; import { tierMatrix } from "@server/lib/billing/tierMatrix";
const verifyResourceSessionSchema = z.object({ const verifyResourceSessionSchema = z.object({

View File

@@ -347,7 +347,7 @@ export async function validateOidcCallback(
allOrgs[0].orgId, allOrgs[0].orgId,
tierMatrix.autoProvisioning tierMatrix.autoProvisioning
); );
if (subscribed) { if (!subscribed) {
return next( return next(
createHttpError( createHttpError(
HttpCode.FORBIDDEN, HttpCode.FORBIDDEN,

View File

@@ -132,7 +132,7 @@ export async function createOrgUser(
orgId, orgId,
tierMatrix.orgOidc tierMatrix.orgOidc
); );
if (subscribed) { if (!subscribed) {
return next( return next(
createHttpError( createHttpError(
HttpCode.FORBIDDEN, HttpCode.FORBIDDEN,

View File

@@ -17,6 +17,7 @@ import m9 from "./scriptsPg/1.12.0";
import m10 from "./scriptsPg/1.13.0"; import m10 from "./scriptsPg/1.13.0";
import m11 from "./scriptsPg/1.14.0"; import m11 from "./scriptsPg/1.14.0";
import m12 from "./scriptsPg/1.15.0"; import m12 from "./scriptsPg/1.15.0";
import m13 from "./scriptsPg/1.15.3";
// THIS CANNOT IMPORT ANYTHING FROM THE SERVER // THIS CANNOT IMPORT ANYTHING FROM THE SERVER
// EXCEPT FOR THE DATABASE AND THE SCHEMA // EXCEPT FOR THE DATABASE AND THE SCHEMA
@@ -34,7 +35,8 @@ const migrations = [
{ version: "1.12.0", run: m9 }, { version: "1.12.0", run: m9 },
{ version: "1.13.0", run: m10 }, { version: "1.13.0", run: m10 },
{ version: "1.14.0", run: m11 }, { version: "1.14.0", run: m11 },
{ version: "1.15.0", run: m12 } { version: "1.15.0", run: m12 },
{ version: "1.15.3", run: m13 }
// Add new migrations here as they are created // Add new migrations here as they are created
] as { ] as {
version: string; version: string;

View File

@@ -35,6 +35,7 @@ import m30 from "./scriptsSqlite/1.12.0";
import m31 from "./scriptsSqlite/1.13.0"; import m31 from "./scriptsSqlite/1.13.0";
import m32 from "./scriptsSqlite/1.14.0"; import m32 from "./scriptsSqlite/1.14.0";
import m33 from "./scriptsSqlite/1.15.0"; import m33 from "./scriptsSqlite/1.15.0";
import m34 from "./scriptsSqlite/1.15.3";
// THIS CANNOT IMPORT ANYTHING FROM THE SERVER // THIS CANNOT IMPORT ANYTHING FROM THE SERVER
// EXCEPT FOR THE DATABASE AND THE SCHEMA // EXCEPT FOR THE DATABASE AND THE SCHEMA
@@ -68,7 +69,8 @@ const migrations = [
{ version: "1.12.0", run: m30 }, { version: "1.12.0", run: m30 },
{ version: "1.13.0", run: m31 }, { version: "1.13.0", run: m31 },
{ version: "1.14.0", run: m32 }, { version: "1.14.0", run: m32 },
{ version: "1.15.0", run: m33 } { version: "1.15.0", run: m33 },
{ version: "1.15.3", run: m34 }
// Add new migrations here as they are created // Add new migrations here as they are created
] as const; ] as const;

View File

@@ -0,0 +1,39 @@
import { db } from "@server/db/pg/driver";
import { sql } from "drizzle-orm";
import { __DIRNAME } from "@server/lib/consts";
const version = "1.15.3";
export default async function migration() {
console.log(`Running setup script ${version}...`);
try {
await db.execute(sql`BEGIN`);
await db.execute(
sql`ALTER TABLE "limits" ADD COLUMN "override" boolean DEFAULT false;`
);
await db.execute(
sql`ALTER TABLE "subscriptionItems" ADD COLUMN "stripeSubscriptionItemId" varchar(255);`
);
await db.execute(
sql`ALTER TABLE "subscriptionItems" ADD COLUMN "featureId" varchar(255);`
);
await db.execute(
sql`ALTER TABLE "subscriptions" ADD COLUMN "version" integer;`
);
await db.execute(
sql`ALTER TABLE "subscriptions" ADD COLUMN "type" varchar(50);`
);
await db.execute(sql`COMMIT`);
console.log("Migrated database");
} catch (e) {
await db.execute(sql`ROLLBACK`);
console.log("Unable to migrate database");
console.log(e);
throw e;
}
console.log(`${version} migration complete`);
}

View File

@@ -0,0 +1,28 @@
import { __DIRNAME, APP_PATH } from "@server/lib/consts";
import Database from "better-sqlite3";
import path from "path";
const version = "1.15.3";
export default async function migration() {
console.log(`Running setup script ${version}...`);
const location = path.join(APP_PATH, "db", "db.sqlite");
const db = new Database(location);
try {
db.transaction(() => {
db.prepare(`ALTER TABLE 'limits' ADD 'override' integer DEFAULT false;`).run();
db.prepare(`ALTER TABLE 'subscriptionItems' ADD 'featureId' text;`).run();
db.prepare(`ALTER TABLE 'subscriptions' ADD 'version' integer;`).run();
db.prepare(`ALTER TABLE 'subscriptions' ADD 'type' text;`).run();
})();
console.log(`Migrated database`);
} catch (e) {
console.log("Failed to migrate db:", e);
throw e;
}
console.log(`${version} migration complete`);
}