feat: allow for unlimitedAllowList + typedefs snuck in by accident (#1813)
Docker Image CI / build-and-push-image (push) Has been cancelled
Maintain Release Merge PR / update-release-pr (push) Has been cancelled
release-please / release-please (push) Has been cancelled
test / test (20.x) (push) Has been cancelled
test / test (22.x) (push) Has been cancelled
test / api-test (22.x) (push) Has been cancelled

* feat: allow for unlimitedAllowList

* feat: add clearer types to extensions
This commit is contained in:
Daniel Salazar
2025-10-23 13:57:32 -07:00
committed by GitHub
parent ce38983e73
commit 219fd2274c
5 changed files with 52 additions and 35 deletions
+40 -30
View File
@@ -1,51 +1,61 @@
import type { Actor } from '@heyputer/backend/src/services/auth/Actor.js';
import type { MeteringService } from '@heyputer/backend/src/services/MeteringService/MeteringService.ts';
import type { DBKVStore } from '@heyputer/backend/src/services/repositories/DBKVStore/DBKVStore.ts';
import type { SUService } from '@heyputer/backend/src/services/SUService.js';
import type { RequestHandler } from 'express';
import type helpers from '../src/backend/src/helpers.js';
declare global {
namespace Express {
interface Request {
actor: any; // TODO
namespace Express {
interface Request {
services: { get: <T extends (keyof ServiceNameMap ) | (string & {})>(string: T)=> T extends keyof ServiceNameMap ? ServiceNameMap[T] : unknown }
actor: Actor
}
}
}
}
type EndpointOptions = {
allowedMethods?: string[]
subdomain?: string
noauth?: boolean
interface EndpointOptions {
allowedMethods?: string[]
subdomain?: string
noauth?: boolean
}
type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch';
type AddRouteFunction = (path: string, options: EndpointOptions, handler: RequestHandler) => void
export type AddRouteFunction = (path: string, options: EndpointOptions, handler: RequestHandler) => void;
type RouterMethods = {
[K in HttpMethod]: {
(path: string, options: EndpointOptions, handler: RequestHandler): void;
(path: string, handler: RequestHandler, options?: EndpointOptions): void;
};
[K in HttpMethod]: {
(path: string, options: EndpointOptions, handler: RequestHandler): void;
(path: string, handler: RequestHandler, options?: EndpointOptions): void;
};
};
interface CoreRuntimeModule {
util: {
helpers: typeof helpers,
}
}
type CoreRuntimeModule = {
util: {
helpers: typeof helpers,
}
}
type StripPrefix<TPrefix extends string, T extends string> = T extends `${TPrefix}.${infer R}` ? R : never;
// TODO DS: define this globally in core to use it there too
interface ServiceNameMap {
'meteringService': { meteringService: MeteringService } & MeteringService // TODO DS: squash into a single class without wrapper
'puter-kv': DBKVStore
'su': SUService
}
interface Extension extends RouterMethods {
// import(module: 'core'): {
// UserActorType: typeof UserActorType;
// };
import(module: 'core'): CoreRuntimeModule;
import(module: string): any;
import<T extends (`service:${keyof ServiceNameMap}` | 'core') | (string & {})>(module: T): T extends `service:${infer R extends keyof ServiceNameMap}`
? ServiceNameMap[R]
: T extends 'core'
? CoreRuntimeModule
: unknown;
}
declare global {
// Declare the extension variable
const extension: Extension;
const config: Record<string | number | symbol, unknown>;
const global_config: Record<string | number | symbol, unknown>;
// Declare the extension variable
const extension: Extension;
const config: Record<string | number | symbol, unknown>;
const global_config: Record<string | number | symbol, unknown>;
}
export { };
+3
View File
@@ -1,5 +1,8 @@
{
"unlimitedUsage": false,
"unlimitedAllowList": [
"admin"
],
"allowedGlobalUsageUsers": [
"06ab2f87-aef5-441b-9c60-debbb8d24dda",
"d8fd169b-4e93-484a-bd84-115b5a2f0ed4"
@@ -9,8 +9,7 @@ extension.on('metering:overrideDefaultSubscription', async (/** @type {{actor: i
extension.on('metering:registerAvailablePolicies', async (
/** @type {{actor: import('@heyputer/backend/src/services/auth/Actor').Actor, availablePolicies: unknown[]}} */event) => {
// bit of a stub implementation for OSS, technically can be always free if you set this config true
if ( config.unlimitedUsage ) {
console.warn('WARNING!!! unlimitedUsage is enabled, this is not recommended for production use');
if ( config.unlimitedUsage || config.unlimitedAllowList?.length ) {
event.availablePolicies.push({
id: 'unlimited',
monthUsageAllowance: 5_000_000 * 1_000_000 * 100, // unless you're like, jeff's, mark's, and elon's illegitamate son, you probably won't hit $5m a month
@@ -20,6 +19,13 @@ extension.on('metering:registerAvailablePolicies', async (
});
extension.on('metering:getUserSubscription', async (/** @type {{actor: import('@heyputer/backend/src/services/auth/Actor').Actor, userSubscriptionId: string}} */event) => {
event.userSubscriptionId = event?.actor?.type?.user?.subscription?.active ? event.actor.type.user.subscription?.tier : undefined;
const userName = event?.actor?.type?.user?.username;
if ( config.unlimitedAllowList?.includes(userName) ) {
console.warn(`WARNING!!! User ${userName} is on unlimited usage allow list, this is not recommended for production use`);
event.userSubscriptionId;
}
else {
event.userSubscriptionId = event?.actor?.type?.user?.subscription?.active ? event.actor.type.user.subscription?.tier : undefined;
}
// default location for user sub, but can techinically be anywhere else or fetched on request
});
-1
View File
@@ -1,4 +1,3 @@
/** @type {import('@heyputer/backend/src/services/MeteringService/MeteringServiceWrapper.mjs').MeteringServiceWrapper} */
const meteringServiceWrapper = extension.import('service:meteringService');
// TODO DS: move this to its own router and just use under this path
-1
View File
@@ -17,6 +17,5 @@
"**/tests/**",
"node_modules",
"dist",
"extensions"
]
}