diff --git a/src/backend/src/CoreModule.js b/src/backend/src/CoreModule.js
index b4d37e8de..0847124c1 100644
--- a/src/backend/src/CoreModule.js
+++ b/src/backend/src/CoreModule.js
@@ -110,7 +110,6 @@ const install = async ({ services, app, useapi, modapi }) => {
const { NAPIThumbnailService } = require('./services/thumbnails/NAPIThumbnailService');
const { DevConsoleService } = require('./services/DevConsoleService');
const { RateLimitService } = require('./services/sla/RateLimitService');
- const { MonthlyUsageService } = require('./services/sla/MonthlyUsageService');
const { AuthService } = require('./services/auth/AuthService');
const { SLAService } = require('./services/sla/SLAService');
const { PermissionService } = require('./services/auth/PermissionService');
@@ -221,7 +220,6 @@ const install = async ({ services, app, useapi, modapi }) => {
]),
})
services.registerService('rate-limit', RateLimitService);
- services.registerService('monthly-usage', MonthlyUsageService);
services.registerService('auth', AuthService);
services.registerService('permission', PermissionService);
services.registerService('sla', SLAService);
diff --git a/src/backend/src/api/APIError.js b/src/backend/src/api/APIError.js
index 195516662..afd1a22fe 100644
--- a/src/backend/src/api/APIError.js
+++ b/src/backend/src/api/APIError.js
@@ -336,16 +336,6 @@ module.exports = class APIError {
message: ({ method_name, rate_limit }) =>
`Rate limit exceeded for method ${quot(method_name)}: ${rate_limit.max} requests per ${rate_limit.period}ms.`,
},
- 'monthly_limit_exceeded': {
- status: 429,
- message: ({ method_key, limit }) =>
- `Monthly limit exceeded for method ${quot(method_key)}: ${limit} requests per month.`,
- },
- 'monthly_usage_exceeded': {
- status: 429,
- message: ({ limit, unit }) =>
- `Monthly limit exceeded: ${limit} ${unit} per month.`,
- },
'server_rate_exceeded': {
status: 503,
message: 'System-wide rate limit exceeded. Please try again later.',
diff --git a/src/backend/src/routers/drivers/usage.js b/src/backend/src/routers/drivers/usage.js
index 71b69c8ca..22d98eb39 100644
--- a/src/backend/src/routers/drivers/usage.js
+++ b/src/backend/src/routers/drivers/usage.js
@@ -55,136 +55,9 @@ module.exports = eggspress('/drivers/usage', {
await svc_event.emit('usages.query', event);
usages.usages = event.usages;
- const rows = await db.read(
- 'SELECT * FROM `service_usage_monthly` WHERE user_id=? ' +
- 'AND `year` = ? AND `month` = ?',
- [
- actor.type.user.id,
- new Date().getUTCFullYear(),
- new Date().getUTCMonth() + 1,
- ]
- );
const user_is_verified = actor.type.user.email_confirmed;
- for ( const row of rows ) {
- const app = await get_app({ id: row.app_id });
-
- let extra_parsed;
- try {
- extra_parsed = db.case({
- mysql: () => row.extra,
- otherwise: () => JSON.parse(row.extra),
- })();
- } catch ( e ) {
- console.log(
- '\x1B[31;1m error parsing monthly usage extra',
- row.extra, e,
- );
- continue;
- }
-
- const identifying_fields = {
- service: extra_parsed,
- year: row.year,
- month: row.month,
- };
-
- // EntityStorage identifiers weren't tracked properly. We don't realy need
- // to track or show them, so this isn't a huge deal, but we need to make
- // sure they don't populate garbage data into the usage report.
- if ( ! identifying_fields.service['driver.implementation'] ) {
- continue;
- }
- if ( identifying_fields.service['driver.interface'] === 'puter-es' ) {
- continue;
- }
- if ( identifying_fields.service['driver.interface'] === 'puter-kvstore' ) {
- continue;
- }
-
- const svc_driverUsage = req.services.get('driver-usage-policy');
- const policy = await svc_driverUsage.get_effective_policy({
- actor,
- service_name: identifying_fields.service['driver.implementation'],
- trait_name: identifying_fields.service['driver.interface'],
- });
-
- // console.log(`POLICY FOR ${identifying_fields.service['driver.implementation']} ${identifying_fields.service['driver.interface']}`, policy);
-
- const user_usage_key = hash_serializable_object(identifying_fields);
-
- if ( ! usages.user[user_usage_key] ) {
- usages.user[user_usage_key] = {
- ...identifying_fields,
- policy,
- };
- usages.user[user_usage_key].monthly_limit =
- policy?.['monthly-limit'] ??
- policy?.['monthy-limit'] ??
- null;
- if ( ! policy ) {
- usages.user[user_usage_key].monthly_limit = 0;
- }
- }
-
- usages.user[user_usage_key].monthly_usage =
- (usages.user[user_usage_key].monthly_usage || 0) + row.count;
-
- // const user_method_usage = usages.user.find(
- // u => u.key === row.key
- // );
-
- // This will be
- if ( ! app ) continue;
-
- const app_usages = usages.apps[app.uid]
- ?? (usages.apps[app.uid] = {});
-
- const id_plus_app = {
- ...identifying_fields,
- app_uid: app.uid,
- };
-
- usages.app_objects[app.uid] = app;
-
- const app_usage_key = hash_serializable_object(id_plus_app);
-
- // DISABLED FOR NOW: need to rework this for the new policy system
- if ( false ) if ( ! app_usages[app_usage_key] ) {
- app_usages[app_usage_key] = {
- ...identifying_fields,
- };
-
- const method_key = row.extra['driver.implementation'] +
- ':' + row.extra['driver.method'];
- const sla_key = `driver:impl:${method_key}`;
-
- const svc_sla = x.get('services').get('sla');
- const sla = await svc_sla.get('app_default', sla_key);
-
- app_usages[app_usage_key].monthly_limit =
- sla?.monthly_limit || null;
- }
-
- // TODO: DRY
- // remove some privileged information
- delete app.id;
- delete app.approved_for_listing;
- delete app.approved_for_opening_items;
- delete app.godmode;
- delete app.owner_user_id;
-
- if ( ! app_usages[app_usage_key] ) {
- app_usages[app_usage_key] = {};
- }
-
- app_usages[app_usage_key].monthly_usage =
- (app_usages[app_usage_key].monthly_usage || 0) + row.count;
-
- // usages.apps.push(usage);
- }
-
for ( const k in usages.apps ) {
usages.apps[k] = Object.values(usages.apps[k]);
}
diff --git a/src/backend/src/services/drivers/DriverService.js b/src/backend/src/services/drivers/DriverService.js
index 2a15bcfbc..cf3a15eca 100644
--- a/src/backend/src/services/drivers/DriverService.js
+++ b/src/backend/src/services/drivers/DriverService.js
@@ -429,42 +429,6 @@ class DriverService extends BaseService {
return args;
},
},
- {
- name: 'enforce monthly usage limit',
- on_call: async args => {
- if ( skip_usage ) return args;
-
- // Typo-Tolerance
- if ( effective_policy?.['monthy-limit'] ) {
- effective_policy['monthly-limit'] = effective_policy['monthy-limit'];
- }
-
- if ( ! effective_policy?.['monthly-limit'] ) return args;
- const svc_monthlyUsage = services.get('monthly-usage');
- const count = await svc_monthlyUsage.check_2(
- actor, method_key, 0
- );
- if ( count >= effective_policy['monthly-limit'] ) {
- throw APIError.create('monthly_limit_exceeded', null, {
- method_key,
- limit: effective_policy['monthly-limit'],
- });
- }
- return args;
- },
- on_return: async result => {
- if ( skip_usage ) return result;
-
- const svc_monthlyUsage = services.get('monthly-usage');
- const extra = {
- 'driver.interface': iface,
- 'driver.implementation': service_name,
- 'driver.method': method,
- };
- await svc_monthlyUsage.increment(actor, method_key, extra);
- return result;
- },
- },
{
name: 'add metadata',
on_return: async result => {
diff --git a/src/backend/src/services/sla/MonthlyUsageService.js b/src/backend/src/services/sla/MonthlyUsageService.js
deleted file mode 100644
index 39f66fd46..000000000
--- a/src/backend/src/services/sla/MonthlyUsageService.js
+++ /dev/null
@@ -1,247 +0,0 @@
-// METADATA // {"ai-commented":{"service":"xai"}}
-/*
- * Copyright (C) 2024-present Puter Technologies Inc.
- *
- * This file is part of Puter.
- *
- * Puter is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-const BaseService = require("../BaseService");
-const { UserActorType, AppUnderUserActorType } = require("../auth/Actor");
-const { DB_WRITE } = require("../database/consts");
-
-
-/**
-* MonthlyUsageService - A service class for managing and tracking monthly usage statistics.
-*
-* This class extends BaseService to provide functionalities related to:
-* - Incrementing usage counts for actors (users or applications under users).
-* - Checking current usage against specified criteria for both users and applications.
-* - Handling different types of actors (UserActorType, AppUnderUserActorType) to ensure
-* appropriate data segregation and usage limits enforcement.
-*
-* @extends BaseService
-*/
-class MonthlyUsageService extends BaseService {
- /**
- * Initializes the MonthlyUsageService by setting up the database connection.
- *
- * @note This method sets the `db` property to a write-enabled database connection for usage data.
- */
- async _init () {
- this.db = this.services.get('database').get(DB_WRITE, 'usage');
- }
-
-
- /**
- * Increments the usage count for a specific actor and key.
- *
- * @param {Object} actor - The actor object whose usage is being tracked.
- * @param {string} key - The usage key to increment.
- * @param {Object} extra - Additional metadata to store with the usage record.
- *
- * @note This method generates a unique key based on the actor's UID and the provided key.
- * @note The method performs an UPSERT operation, ensuring the count is incremented or set to 1 if new.
- * @note The `extra` parameter is stringified before being stored in the database.
- * @returns {Promise} - A promise that resolves when the increment operation is complete.
- */
- async increment (actor, key, extra) {
- key = `${actor.uid}:${key}`;
-
- const year = new Date().getUTCFullYear();
- // months are zero-indexed by getUTCMonth, which could be confusing
- const month = new Date().getUTCMonth() + 1;
-
- const maybe_app_id = actor.type.app?.id;
- const stringified = JSON.stringify(extra);
-
- // UPSERT increment count
- await this.db.write(
- 'INSERT INTO `service_usage_monthly` (`year`, `month`, `key`, `count`, `user_id`, `app_id`, `extra`) ' +
- 'VALUES (?, ?, ?, 1, ?, ?, ?) ' +
- this.db.case({
- mysql: 'ON DUPLICATE KEY UPDATE `count` = `count` + 1, `extra` = ?',
- // sqlite: ' ',
- otherwise: 'ON CONFLICT(`year`, `month`, `key`) ' +
- 'DO UPDATE SET `count` = `count` + 1, `extra` = ?',
- }),
- [
- year, month, key, actor.type.user.id, maybe_app_id ?? null, stringified,
- stringified,
- ]
- );
- }
-
-
- /**
- * Checks the monthly usage for the given actor based on specific criteria.
- *
- * This method determines the type of actor and delegates the check to the appropriate
- * method for further processing. It supports both user and app-under-user actors.
- *
- * @param {Object} actor - The actor whose usage needs to be checked.
- * @param {Object} specifiers - JSON object specifying conditions for the usage check.
- * @returns {Promise} The total usage count or 0 if no matching records found.
- */
- async check (actor, specifiers) {
- if ( actor.type instanceof UserActorType ) {
- return await this._user_check(actor, specifiers);
- }
-
- if ( actor.type instanceof AppUnderUserActorType ) {
- return await this._app_under_user_check(actor, specifiers);
- }
-
- }
-
-
- /**
- * Checks usage for an actor, routing to specific check methods based on actor type.
- * @param {Object} actor - The actor to check usage for.
- * @param {Object} specifiers - Additional specifiers for the usage check.
- * @returns {Promise} The usage count or 0 if no usage is found.
- */
- async check_2 (actor, key, ver) {
- // TODO: get 'ver' working here for future updates
- key = `${actor.uid}:${key}`;
- if ( actor.type instanceof UserActorType ) {
- return await this._user_check_2(actor, key);
- }
-
- if ( actor.type instanceof AppUnderUserActorType ) {
- return await this._app_under_user_check_2(actor, key);
- }
-
- }
-
-
- /**
- * Performs a secondary check on usage for either user or app under user actors.
- *
- * @param {Object} actor - The actor performing the action.
- * @param {string} key - The usage key to check.
- * @param {string} ver - The version, currently not implemented for future updates.
- * @returns {Promise} A promise that resolves to the count of usage, or 0 if not found.
- * @note The 'ver' parameter is planned for future use to handle version-specific checks.
- */
- async _user_check (actor, specifiers) {
- const year = new Date().getUTCFullYear();
- // months are zero-indexed by getUTCMonth, which could be confusing
- const month = new Date().getUTCMonth() + 1;
-
- const rows = await this.db.read(
- 'SELECT SUM(`count`) AS sum FROM `service_usage_monthly` ' +
- 'WHERE `year` = ? AND `month` = ? AND `user_id` = ? ' +
- 'AND JSON_CONTAINS(`extra`, ?)',
- [
- year, month, actor.type.user.id,
- JSON.stringify(specifiers),
- ]
- );
-
- return rows[0]?.sum || 0;
- }
-
-
- /**
- * Performs a usage check for a user based on a specific key.
- *
- * @param {Object} actor - The actor object representing the user or app.
- * @param {string} key - The unique key to check usage for.
- * @returns {Promise} The sum of usage count for the specified key, or 0 if not found.
- * @note This method is intended for future updates where version control might be implemented.
- */
- async _user_check_2 (actor, key) {
- const year = new Date().getUTCFullYear();
- // months are zero-indexed by getUTCMonth, which could be confusing
- const month = new Date().getUTCMonth() + 1;
-
- const rows = await this.db.read(
- 'SELECT SUM(`count`) AS sum FROM `service_usage_monthly` ' +
- 'WHERE `year` = ? AND `month` = ? AND `user_id` = ? ' +
- 'AND `key` = ?',
- [
- year, month, actor.type.user.id,
- key,
- ]
- );
-
- return rows[0]?.sum || 0;
- }
-
-
- /**
- * Checks the monthly usage for an app under a user account.
- *
- * @param {Object} actor - The actor object representing the user and app context.
- * @param {Object} specifiers - An object containing usage specifiers to filter the query.
- * @returns {Promise} - The count of usage for the specified criteria or 0 if not found.
- * @note This method queries the database for usage data specific to an app within a user's account.
- * It uses JSON_CONTAINS to match specifiers within the extra field of the database entry.
- */
- async _app_under_user_check (actor, specifiers) {
- const year = new Date().getUTCFullYear();
- // months are zero-indexed by getUTCMonth, which could be confusing
- const month = new Date().getUTCMonth() + 1;
-
- // SELECT count
- const rows = await this.db.read(
- 'SELECT `count` FROM `service_usage_monthly` ' +
- 'WHERE `year` = ? AND `month` = ? AND `user_id` = ? ' +
- 'AND `app_id` = ? ' +
- 'AND JSON_CONTAINS(`extra`, ?)',
- [
- year, month, actor.type.user.id,
- actor.type.app.id,
- specifiers,
- ]
- );
-
- return rows[0]?.count || 0;
- }
-
-
- /**
- * Performs a check for usage under an app, identified by a specific key.
- * This method queries the database to retrieve the usage count for a given user, app, and key.
- *
- * @param {Actor} actor - The actor object containing user and app information.
- * @param {string} key - The usage key to check against.
- * @returns {Promise} - A promise that resolves to the usage count or 0 if no record exists.
- */
- async _app_under_user_check_2 (actor, key) {
- const year = new Date().getUTCFullYear();
- // months are zero-indexed by getUTCMonth, which could be confusing
- const month = new Date().getUTCMonth() + 1;
-
- // SELECT count
- const rows = await this.db.read(
- 'SELECT `count` FROM `service_usage_monthly` ' +
- 'WHERE `year` = ? AND `month` = ? AND `user_id` = ? ' +
- 'AND `app_id` = ? ' +
- 'AND `key` = ?',
- [
- year, month, actor.type.user.id,
- actor.type.app.id,
- key,
- ]
- );
-
- return rows[0]?.count || 0;
- }
-}
-
-module.exports = {
- MonthlyUsageService,
-};
diff --git a/src/backend/src/services/sla/SLAService.js b/src/backend/src/services/sla/SLAService.js
index 07339bfce..3453dfb4a 100644
--- a/src/backend/src/services/sla/SLAService.js
+++ b/src/backend/src/services/sla/SLAService.js
@@ -57,7 +57,6 @@ class SLAService extends BaseService {
max: 10,
period: 30000,
},
- monthly_limit: 80 * 1000,
},
},
// app_default: {
@@ -89,21 +88,18 @@ class SLAService extends BaseService {
max: 40,
period: 30000,
},
- monthly_limit: 20,
},
'driver:impl:public-openai-chat-completion:complete': {
rate_limit: {
max: 40,
period: 30000,
},
- monthly_limit: 100,
},
'driver:impl:public-openai-image-generation:generate': {
rate_limit: {
max: 40,
period: 30000,
},
- monthly_limit: 4,
},
},
user_verified: {
@@ -112,21 +108,18 @@ class SLAService extends BaseService {
max: 40,
period: 30000,
},
- monthly_limit: 100,
},
'driver:impl:public-openai-chat-completion:complete': {
rate_limit: {
max: 40,
period: 30000,
},
- monthly_limit: 3000,
},
'driver:impl:public-openai-image-generation:generate': {
rate_limit: {
max: 40,
period: 30000,
},
- monthly_limit: 5,
},
}
};