Add panel feature updates across API, daemon, and web

This commit is contained in:
2026-03-02 21:53:54 +00:00
parent 6b463c2b1a
commit afc64b83c1
49 changed files with 7040 additions and 305 deletions
@@ -22,6 +22,34 @@
"when": 1772300000000,
"tag": "0002_cs2_add_metamod_workflow",
"breakpoints": true
},
{
"idx": 3,
"version": "7",
"when": 1772400000000,
"tag": "0003_global_plugin_registry",
"breakpoints": true
},
{
"idx": 4,
"version": "7",
"when": 1772600000000,
"tag": "0004_cs2_startup_parameters",
"breakpoints": true
},
{
"idx": 5,
"version": "7",
"when": 1772800000000,
"tag": "0005_cs2_servername_default",
"breakpoints": true
},
{
"idx": 6,
"version": "7",
"when": 1772900000000,
"tag": "0006_cs2_servername_branding",
"breakpoints": true
}
]
}
+1
View File
@@ -8,3 +8,4 @@ export * from './backups';
export * from './plugins';
export * from './schedules';
export * from './audit-logs';
export * from './server-databases';
+46
View File
@@ -6,11 +6,17 @@ import {
boolean,
timestamp,
pgEnum,
jsonb,
bigint,
} from 'drizzle-orm/pg-core';
import { games } from './games';
import { servers } from './servers';
import { users } from './users';
export const pluginSourceEnum = pgEnum('plugin_source', ['spiget', 'manual']);
export const pluginReleaseChannelEnum = pgEnum('plugin_release_channel', ['stable', 'beta', 'alpha']);
export const pluginReleaseArtifactTypeEnum = pgEnum('plugin_release_artifact_type', ['file', 'zip']);
export const pluginInstallStatusEnum = pgEnum('plugin_install_status', ['installed', 'updating', 'failed']);
export const plugins = pgTable('plugins', {
id: uuid('id').defaultRandom().primaryKey(),
@@ -24,6 +30,29 @@ export const plugins = pgTable('plugins', {
externalId: varchar('external_id', { length: 255 }),
downloadUrl: text('download_url'),
version: varchar('version', { length: 100 }),
isGlobal: boolean('is_global').default(true).notNull(),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
});
export const pluginReleases = pgTable('plugin_releases', {
id: uuid('id').defaultRandom().primaryKey(),
pluginId: uuid('plugin_id')
.notNull()
.references(() => plugins.id, { onDelete: 'cascade' }),
version: varchar('version', { length: 100 }).notNull(),
channel: pluginReleaseChannelEnum('channel').default('stable').notNull(),
artifactType: pluginReleaseArtifactTypeEnum('artifact_type').default('file').notNull(),
artifactUrl: text('artifact_url').notNull(),
destination: text('destination'),
fileName: varchar('file_name', { length: 255 }),
checksumSha256: varchar('checksum_sha256', { length: 128 }),
sizeBytes: bigint('size_bytes', { mode: 'number' }),
changelog: text('changelog'),
installSchema: jsonb('install_schema').default([]).notNull(),
configTemplates: jsonb('config_templates').default([]).notNull(),
isPublished: boolean('is_published').default(true).notNull(),
createdByUserId: uuid('created_by_user_id').references(() => users.id, { onDelete: 'set null' }),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
});
@@ -36,7 +65,24 @@ export const serverPlugins = pgTable('server_plugins', {
pluginId: uuid('plugin_id')
.notNull()
.references(() => plugins.id, { onDelete: 'cascade' }),
releaseId: uuid('release_id').references(() => pluginReleases.id, { onDelete: 'set null' }),
installedVersion: varchar('installed_version', { length: 100 }),
isActive: boolean('is_active').default(true).notNull(),
installOptions: jsonb('install_options').default({}).notNull(),
autoUpdateChannel: pluginReleaseChannelEnum('auto_update_channel').default('stable').notNull(),
isPinned: boolean('is_pinned').default(false).notNull(),
status: pluginInstallStatusEnum('status').default('installed').notNull(),
lastError: text('last_error'),
installedAt: timestamp('installed_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
});
export const serverPluginFiles = pgTable('server_plugin_files', {
id: uuid('id').defaultRandom().primaryKey(),
serverPluginId: uuid('server_plugin_id')
.notNull()
.references(() => serverPlugins.id, { onDelete: 'cascade' }),
path: text('path').notNull(),
kind: varchar('kind', { length: 32 }).default('artifact').notNull(),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
});
@@ -0,0 +1,25 @@
import {
pgTable,
uuid,
varchar,
text,
integer,
timestamp,
} from 'drizzle-orm/pg-core';
import { servers } from './servers';
export const serverDatabases = pgTable('server_databases', {
id: uuid('id').defaultRandom().primaryKey(),
serverId: uuid('server_id')
.notNull()
.references(() => servers.id, { onDelete: 'cascade' }),
name: varchar('name', { length: 255 }).notNull(),
databaseName: varchar('database_name', { length: 255 }).notNull().unique(),
username: varchar('username', { length: 64 }).notNull().unique(),
password: text('password').notNull(),
host: varchar('host', { length: 255 }).notNull(),
port: integer('port').notNull(),
phpMyAdminUrl: text('phpmyadmin_url'),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
});
+134 -1
View File
@@ -2,6 +2,57 @@ import { createDb } from './client';
import { games } from './schema/games';
import { users } from './schema/users';
const DEFAULT_CS2_SERVER_CFG = `// ============================================
// CS2 Server Config
// ============================================
// ---- Sunucu Bilgileri ----
hostname "SourceGamePanel CS2 Server"
sv_password ""
rcon_password "changeme"
sv_cheats 0
// ---- Topluluk Sunucu Gorunurlugu ----
sv_region 3
sv_tags "competitive,community"
sv_lan 0
sv_steamgroup ""
sv_steamgroup_exclusive 0
// ---- Performans ----
sv_maxrate 0
sv_minrate 64000
sv_max_queries_sec 5
sv_max_queries_window 30
sv_parallel_sendsnapshot 1
net_maxroutable 1200
// ---- Baglanti ----
sv_maxclients 16
sv_timeout 60
// ---- GOTV (Tamamen Kapali) ----
tv_enable 0
tv_autorecord 0
tv_delay 0
tv_maxclients 0
tv_port 0
// ---- Loglama ----
log on
mp_logmoney 0
mp_logdetail 0
mp_logdetail_items 0
sv_logfile 1
// ---- Genel Oyun Ayarlari ----
mp_autokick 0
sv_allow_votes 0
sv_alltalk 0
sv_deadtalk 1
sv_voiceenable 1
`;
async function seed() {
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) {
@@ -101,9 +152,33 @@ async function seed() {
parser: 'keyvalue',
editableKeys: [
'hostname',
'sv_tags',
'sv_password',
'rcon_password',
'sv_cheats',
'sv_region',
'sv_lan',
'sv_steamgroup',
'sv_steamgroup_exclusive',
'sv_maxrate',
'sv_minrate',
'sv_max_queries_sec',
'sv_max_queries_window',
'sv_parallel_sendsnapshot',
'net_maxroutable',
'sv_maxclients',
'sv_timeout',
'tv_enable',
'tv_autorecord',
'tv_delay',
'tv_maxclients',
'tv_port',
'sv_logfile',
'mp_autokick',
'sv_allow_votes',
'sv_alltalk',
'sv_deadtalk',
'sv_voiceenable',
'mp_autoteambalance',
'mp_limitteams',
],
@@ -111,6 +186,27 @@ async function seed() {
{ path: 'game/csgo/cfg/autoexec.cfg', parser: 'keyvalue' },
],
automationRules: [
{
id: 'cs2-write-default-server-config',
event: 'server.install.completed',
enabled: true,
runOncePerServer: true,
continueOnError: false,
actions: [
{
id: 'write-cs2-default-server-config',
type: 'write_file',
path: '/game/csgo/cfg/server.cfg',
data: DEFAULT_CS2_SERVER_CFG,
},
{
id: 'write-cs2-persisted-server-config',
type: 'write_file',
path: '/game/csgo/cfg/.sourcegamepanel-server.cfg',
data: DEFAULT_CS2_SERVER_CFG,
},
],
},
{
id: 'cs2-install-latest-metamod',
event: 'server.install.completed',
@@ -168,7 +264,12 @@ async function seed() {
description: 'Steam Game Server Login Token (optional for local testing)',
required: false,
},
{ key: 'CS2_SERVERNAME', default: 'GamePanel CS2 Server', description: 'Server name', required: false },
{
key: 'CS2_SERVERNAME',
default: 'SourceGamePanel CS2 Server',
description: 'Server name',
required: false,
},
{ key: 'CS2_PORT', default: '27015', description: 'Game port', required: false },
{ key: 'CS2_STARTMAP', default: 'de_dust2', description: 'Initial map', required: false },
{ key: 'CS2_MAXPLAYERS', default: '16', description: 'Max players', required: false },
@@ -179,6 +280,38 @@ async function seed() {
description: 'Bind address',
required: false,
},
{
key: 'CS2_HOST_WORKSHOP_COLLECTION',
default: '',
description: 'Steam Workshop collection id to load',
required: false,
},
{
key: 'CS2_HOST_WORKSHOP_MAP',
default: '',
description: 'Steam Workshop map id to launch',
required: false,
},
{ key: 'CS2_GAMETYPE', default: '0', description: 'Game type numeric value', required: false },
{ key: 'CS2_GAMEMODE', default: '1', description: 'Game mode numeric value', required: false },
{
key: 'CS2_ADDITIONAL_ARGS',
default: '',
description: 'Extra startup arguments appended to the server launch command',
required: false,
},
{
key: 'CS2_INSECURE',
label: 'Insecure Mode',
default: '',
description: 'Toggles the -insecure launch flag inside CS2_ADDITIONAL_ARGS',
required: false,
inputType: 'boolean',
composeInto: 'CS2_ADDITIONAL_ARGS',
flagValue: '-insecure',
enabledLabel: 'Aktif',
disabledLabel: 'Pasif',
},
],
},
{
+42
View File
@@ -46,11 +46,49 @@ message CreateServerRequest {
repeated string install_plugin_urls = 9;
}
message UpdateServerRequest {
string uuid = 1;
string docker_image = 2;
int64 memory_limit = 3;
int64 disk_limit = 4;
int32 cpu_limit = 5;
string startup_command = 6;
map<string, string> environment = 7;
repeated PortMapping ports = 8;
}
message ServerResponse {
string uuid = 1;
string status = 2;
}
// === Managed Databases ===
message CreateDatabaseRequest {
string server_uuid = 1;
string name = 2;
string password = 3;
}
message UpdateDatabasePasswordRequest {
string username = 1;
string password = 2;
}
message DeleteDatabaseRequest {
string database_name = 1;
string username = 2;
}
message ManagedDatabaseCredentials {
string database_name = 1;
string username = 2;
string password = 3;
string host = 4;
int32 port = 5;
string phpmyadmin_url = 6;
}
// === Power ===
enum PowerAction {
@@ -210,8 +248,12 @@ service DaemonService {
// Server lifecycle
rpc CreateServer(CreateServerRequest) returns (ServerResponse);
rpc UpdateServer(UpdateServerRequest) returns (ServerResponse);
rpc DeleteServer(ServerIdentifier) returns (Empty);
rpc ReinstallServer(ServerIdentifier) returns (Empty);
rpc CreateDatabase(CreateDatabaseRequest) returns (ManagedDatabaseCredentials);
rpc UpdateDatabasePassword(UpdateDatabasePasswordRequest) returns (Empty);
rpc DeleteDatabase(DeleteDatabaseRequest) returns (Empty);
// Power
rpc SetPowerState(PowerRequest) returns (Empty);
+6
View File
@@ -95,6 +95,12 @@ export interface GameEnvVar {
default: string;
description: string;
required: boolean;
label?: string;
inputType?: 'text' | 'boolean';
composeInto?: string;
flagValue?: string;
enabledLabel?: string;
disabledLabel?: string;
}
export type GameAutomationRule = GameAutomationWorkflow;