-
Notifications
You must be signed in to change notification settings - Fork 261
lighthouse #1109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
lighthouse #1109
Changes from all commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
33291b8
wip on service ping
brendan-kellam dea5249
add concept of License to the database and wire it into the enitlemen…
brendan-kellam 50914b3
wip on e2e checkout flow
brendan-kellam 478c4c9
basic card to display current plan
brendan-kellam 52dab4f
store activation code at rest
brendan-kellam c99b3d0
add manage subscription button
brendan-kellam abe2158
wip on lighthouse client
brendan-kellam 949634f
wip on lighthouse client
brendan-kellam 26dce02
remove the concept of a plan and just rely on entitlements at the app…
brendan-kellam 420d441
fix sidebar
brendan-kellam 12f7507
add temporary refresh license button
brendan-kellam 409b881
clarified org availability design
brendan-kellam 50f7fbd
entitlements naming nit
brendan-kellam 37317e0
remove concept of a GUEST user
brendan-kellam fdf0256
remove anonymous-access entitlement
brendan-kellam cf6aeaf
change offline license to support optional seats
brendan-kellam 41967e1
handle license validation for all paths
brendan-kellam f80a11d
refactor some actions
brendan-kellam cae073d
add explicit service ping calls to user <> organization update paths
brendan-kellam 201f0b2
fix refresh bug with sidebar
brendan-kellam 74dd139
add billing details card
brendan-kellam 6758ced
fix invite redemption when user already a member
brendan-kellam 1dbb9a7
wip on license card
brendan-kellam cf88304
further wip on license card / activaction code card
brendan-kellam f738aff
add recent invoices UI
brendan-kellam e8cc9be
nit on seats terminology
brendan-kellam 3efe8e1
feat(web): surface subscription cancellation on license card
brendan-kellam 3af6374
feat(web): add offline license card to settings
brendan-kellam 9c2b28b
feat(web): banner system for license + permission sync
brendan-kellam a7ec2ef
handle failure case where lighthouse cannot be reached
brendan-kellam 9760419
chore(web): mirror lighthouse trial + checkout schema changes
brendan-kellam de8d57d
feat(web): trial banner + plumbing
brendan-kellam dad1dac
add badges to invoice cards
brendan-kellam d5365ae
add basic billing docs
brendan-kellam c725e3a
refactor(lighthouse): throw ServiceErrorException on ping failure
brendan-kellam 3991baa
feedback
brendan-kellam f875a0e
fix tests
brendan-kellam File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1025,8 +1025,7 @@ | |
| "type": "string", | ||
| "enum": [ | ||
| "OWNER", | ||
| "MEMBER", | ||
| "GUEST" | ||
| "MEMBER" | ||
| ] | ||
| }, | ||
| "createdAt": { | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| --- | ||
| title: Billing | ||
| sidebarTitle: Billing | ||
| --- | ||
|
|
||
| Sourcebot Enterprise is available on monthly and yearly plans. Both are seat-based. This page explains how seats are billed, how changes mid-term are handled, and what happens at renewal. | ||
|
|
||
| ## Seat count | ||
| Your seat count is the number of active users in your Sourcebot instance. Seat usage is reported to Sourcebot on a daily interval, and your subscription quantity is kept in sync with that number. | ||
|
|
||
| ## Monthly plans | ||
| Monthly subscriptions are billed at the start of each billing cycle. Users added mid-cycle are prorated across the remaining days and appear on your next invoice. Users removed mid-cycle take effect at the next cycle. There is no refund for the remainder of the current one. | ||
|
|
||
| In short: you can scale up at any time and pay the prorated difference. Scaling down is effectively free until the cycle rolls over. | ||
|
|
||
| ## Yearly plans | ||
|
|
||
| Yearly subscriptions are billed upfront for a committed seat count. As users are added during the term, the seat count rises but you aren't charged immediately. Every three months we reconcile. Any seats added that quarter are billed, prorated across the quarters remaining in the term. | ||
|
|
||
| Seats only move upward during the term. Shrinking the user count does not refund, and does not reduce the seat count until renewal. At renewal, you're invoiced at your current seat count, and that number becomes the committed baseline for the next year. | ||
|
|
||
| ### Example | ||
|
|
||
| Suppose you start a yearly plan in January with 100 seats. | ||
|
|
||
| - In Q1, your user count grows to 110. At the end of Q1, you're invoiced for 10 seats prorated across the 3 remaining quarters. | ||
| - In Q2, your user count stays at 110. No reconciliation invoice is generated. | ||
| - In Q3, your user count grows to 120. At the end of Q3, you're invoiced for 10 seats prorated across the 1 remaining quarter. | ||
| - In Q4, reconciliation does not generate a charge (there are no remaining quarters to prorate across). | ||
| - At renewal in January, you're invoiced at 120 seats for the next year. 120 becomes the new committed baseline. | ||
|
|
||
| ## Cancellation | ||
|
|
||
| Cancelling a subscription takes effect at the end of the current billing cycle (monthly) or term (yearly). You retain access to Sourcebot Enterprise features until that point. | ||
|
|
||
| ## Questions? | ||
|
|
||
| For billing questions, [contact us](mailto:support@sourcebot.dev). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| import { vi } from 'vitest'; | ||
|
|
||
| export const prisma = { | ||
| license: { | ||
| findUnique: vi.fn().mockResolvedValue(null), | ||
| }, | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import { | ||
| Entitlement, | ||
| _hasEntitlement, | ||
| _getEntitlements, | ||
| } from "@sourcebot/shared"; | ||
| import { prisma } from "./prisma.js"; | ||
| import { SINGLE_TENANT_ORG_ID } from "./constants.js"; | ||
|
|
||
| const getLicense = async () => { | ||
| return prisma.license.findUnique({ | ||
| where: { orgId: SINGLE_TENANT_ORG_ID }, | ||
| }); | ||
| } | ||
|
|
||
| export const hasEntitlement = async (entitlement: Entitlement): Promise<boolean> => { | ||
| const license = await getLicense(); | ||
| return _hasEntitlement(entitlement, license); | ||
| } | ||
|
|
||
| export const getEntitlements = async (): Promise<Entitlement[]> => { | ||
| const license = await getLicense(); | ||
| return _getEntitlements(license); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import { PrismaClient } from "@sourcebot/db"; | ||
| import { getDBConnectionString } from "@sourcebot/shared"; | ||
|
|
||
| export const prisma = new PrismaClient({ | ||
| datasources: { | ||
| db: { | ||
| url: getDBConnectionString(), | ||
| }, | ||
| }, | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,15 @@ | ||
| import { defineConfig } from 'vitest/config'; | ||
| import path from 'path'; | ||
|
|
||
| export default defineConfig({ | ||
| test: { | ||
| environment: 'node', | ||
| watch: false, | ||
| env: { | ||
| DATA_CACHE_DIR: 'test-data' | ||
| } | ||
| }, | ||
| alias: { | ||
| './prisma.js': path.resolve(__dirname, 'src/__mocks__/prisma.ts'), | ||
| }, | ||
| } | ||
| }); |
21 changes: 21 additions & 0 deletions
21
packages/db/prisma/migrations/20260417224042_remove_guest_org_role/migration.sql
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| /* | ||
| Warnings: | ||
|
|
||
| - The values [GUEST] on the enum `OrgRole` will be removed. If these variants are still used in the database, this will fail. | ||
|
|
||
| */ | ||
|
|
||
| -- Remove the guest user and its membership (only holder of GUEST role) | ||
| DELETE FROM "UserToOrg" WHERE "role" = 'GUEST'; | ||
| DELETE FROM "User" WHERE id = '1'; | ||
|
brendan-kellam marked this conversation as resolved.
|
||
|
|
||
| -- AlterEnum | ||
| BEGIN; | ||
| CREATE TYPE "OrgRole_new" AS ENUM ('OWNER', 'MEMBER'); | ||
| ALTER TABLE "UserToOrg" ALTER COLUMN "role" DROP DEFAULT; | ||
| ALTER TABLE "UserToOrg" ALTER COLUMN "role" TYPE "OrgRole_new" USING ("role"::text::"OrgRole_new"); | ||
| ALTER TYPE "OrgRole" RENAME TO "OrgRole_old"; | ||
| ALTER TYPE "OrgRole_new" RENAME TO "OrgRole"; | ||
| DROP TYPE "OrgRole_old"; | ||
| ALTER TABLE "UserToOrg" ALTER COLUMN "role" SET DEFAULT 'MEMBER'; | ||
| COMMIT; | ||
33 changes: 33 additions & 0 deletions
33
packages/db/prisma/migrations/20260424142634_add_license_table/migration.sql
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| -- AlterTable | ||
| ALTER TABLE "Org" ADD COLUMN "trialUsedAt" TIMESTAMP(3); | ||
|
|
||
| -- CreateTable | ||
| CREATE TABLE "License" ( | ||
| "id" TEXT NOT NULL, | ||
| "orgId" INTEGER NOT NULL, | ||
| "activationCode" TEXT NOT NULL, | ||
| "entitlements" TEXT[], | ||
| "seats" INTEGER, | ||
| "status" TEXT, | ||
| "planName" TEXT, | ||
| "unitAmount" INTEGER, | ||
| "currency" TEXT, | ||
| "interval" TEXT, | ||
| "intervalCount" INTEGER, | ||
| "nextRenewalAt" TIMESTAMP(3), | ||
| "nextRenewalAmount" INTEGER, | ||
| "cancelAt" TIMESTAMP(3), | ||
| "trialEnd" TIMESTAMP(3), | ||
| "hasPaymentMethod" BOOLEAN, | ||
| "lastSyncAt" TIMESTAMP(3), | ||
| "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
| "updatedAt" TIMESTAMP(3) NOT NULL, | ||
|
|
||
| CONSTRAINT "License_pkey" PRIMARY KEY ("id") | ||
| ); | ||
|
|
||
| -- CreateIndex | ||
| CREATE UNIQUE INDEX "License_orgId_key" ON "License"("orgId"); | ||
|
|
||
| -- AddForeignKey | ||
| ALTER TABLE "License" ADD CONSTRAINT "License_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "Org"("id") ON DELETE CASCADE ON UPDATE CASCADE; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.