diff --git a/src/backend/clients/database/SqliteDatabaseClient.ts b/src/backend/clients/database/SqliteDatabaseClient.ts index 03604a117..5604e05af 100644 --- a/src/backend/clients/database/SqliteDatabaseClient.ts +++ b/src/backend/clients/database/SqliteDatabaseClient.ts @@ -85,6 +85,7 @@ const AVAILABLE_MIGRATIONS: [number, string[]][] = [ [50, ['0054_sessions_workers.sql']], [50, ['0055_username_nocase_unique.sql']], [51, ['0056_sessions_kind_worker.sql']], + [52, ['0057_add_user_reputation.sql']], ]; export class SqliteDatabaseClient extends AbstractDatabaseClient { diff --git a/src/backend/clients/database/migrations/sqlite/0057_add_user_reputation.sql b/src/backend/clients/database/migrations/sqlite/0057_add_user_reputation.sql new file mode 100644 index 000000000..bb663ef7d --- /dev/null +++ b/src/backend/clients/database/migrations/sqlite/0057_add_user_reputation.sql @@ -0,0 +1,21 @@ +-- 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 . + +-- Abuse v2 reputation score (0–100+) recorded at signup. Mirrors the +-- `reputation smallint DEFAULT 100` column already present in the mysql and +-- postgres baseline migrations. +ALTER TABLE `user` ADD COLUMN `reputation` SMALLINT DEFAULT 100; diff --git a/src/backend/clients/event/types.ts b/src/backend/clients/event/types.ts index a274ef831..3c6e4b0e2 100644 --- a/src/backend/clients/event/types.ts +++ b/src/backend/clients/event/types.ts @@ -130,7 +130,7 @@ export type EventMap = { req?: unknown; data?: unknown; abuse?: unknown; - custom?: unknown; + trail?: Array; [key: string]: unknown; }; 'puter.signup.success': { diff --git a/src/backend/controllers/auth/AuthController.ts b/src/backend/controllers/auth/AuthController.ts index b5afe366a..b3f253928 100644 --- a/src/backend/controllers/auth/AuthController.ts +++ b/src/backend/controllers/auth/AuthController.ts @@ -557,6 +557,9 @@ export class AuthController extends PuterController { message: null, code: null, user_agent: req?.headers?.['user-agent'] ?? null, + // Populated by the abuse extension's v2 harness; persisted to the + // user row below so the signup-time reputation is referable later. + reputation: null as number | null, }; try { await this.clients.event?.emitAndWait( @@ -622,6 +625,11 @@ export class AuthController extends PuterController { // stays hardcoded here. requires_email_confirmation: 1, last_activity_ts: signupSqlTs, + // Record the v2 reputation from this claim (skip if unset so we + // don't clobber an existing score with a placeholder). + ...(validateEvent.reputation != null + ? { reputation: validateEvent.reputation } + : {}), }); // Move from temp group to regular user group @@ -678,6 +686,7 @@ export class AuthController extends PuterController { signup_server: (this.config as { serverId?: string }).serverId, referrer: req.body.referrer ?? null, last_activity_ts: signupSqlTs, + reputation: validateEvent.reputation, } as never); // Add to default group diff --git a/src/backend/stores/user/UserStore.ts b/src/backend/stores/user/UserStore.ts index efee906f6..70f93fef7 100644 --- a/src/backend/stores/user/UserStore.ts +++ b/src/backend/stores/user/UserStore.ts @@ -43,6 +43,8 @@ export interface UserRow { requires_email_confirmation?: boolean; /** Metadata JSON blob; decoded on read when the DB returns it as a string. */ metadata?: Record; + /** Abuse v2 reputation score recorded at signup (DB default 100). */ + reputation?: number; password?: string; [k: string]: unknown; } @@ -324,6 +326,7 @@ export class UserStore extends PuterStore { signup_server?: string | null; referrer?: string | null; last_activity_ts?: string | null; + reputation?: number | null; }): Promise { assertLatin1Writable(fields as Record); const result = await this.clients.db.write( @@ -344,8 +347,9 @@ export class UserStore extends PuterStore { signup_origin, signup_server, referrer, - last_activity_ts) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)${this.clients.db.returningIdClause()}`, + last_activity_ts, + reputation) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)${this.clients.db.returningIdClause()}`, [ fields.username, fields.email, @@ -368,6 +372,8 @@ export class UserStore extends PuterStore { fields.signup_server ?? null, fields.referrer ?? null, fields.last_activity_ts ?? null, + // Default matches the DB column default + v2's STARTING_REPUTATION. + fields.reputation ?? 100, ], ); @@ -577,6 +583,8 @@ export class UserStore extends PuterStore { requires_email_confirmation: asBool( rest.requires_email_confirmation, ), + reputation: + rest.reputation == null ? undefined : Number(rest.reputation), metadata, }; }