config and params are text fields

This commit is contained in:
CroneKorkN 2026-01-18 18:32:07 +01:00
parent 9a65958d2d
commit a9a93d2657
Signed by: cronekorkn
SSH key fingerprint: SHA256:v0410ZKfuO1QHdgKBsdQNF64xmTxOF8osF1LIqwTcVw
16 changed files with 51 additions and 237 deletions

View file

@ -18,11 +18,9 @@ L4D Tools is a Rails web application for managing Left4Dead 2 game servers. User
``` ```
User (Steam ID + username) User (Steam ID + username)
├── ServerTemplate (name) ├── ServerTemplate (name, config, startup_params)
│ ├── TemplateOverlay (overlay + position) │ ├── TemplateOverlay (overlay + position)
│ │ └── Overlay (name, type: system|custom, path) │ │ └── Overlay (name, type: system|custom, slug)
│ ├── ConfigOption (config_key, config_value)
│ └── StartupParam (param_key, param_value)
├── Server (name, port, status, template_id) ├── Server (name, port, status, template_id)
│ └── (references ServerTemplate) │ └── (references ServerTemplate)
└── Activity (action, resource_type, resource_id, details) └── Activity (action, resource_type, resource_id, details)
@ -69,13 +67,15 @@ Add Overlays:
→ Position auto-assigned (0, 1, 2, ...) → Position auto-assigned (0, 1, 2, ...)
→ First overlay = highest priority (mounted last, visible first) → First overlay = highest priority (mounted last, visible first)
Add Config Options: Edit Server Config:
→ Enter key (e.g., "sv_pure") and value (e.g., "2") → Multi-line text area for server.cfg contents
→ Renders as: sv_pure "2" in server.cfg → Enter raw config format: sv_pure 2\nsv_maxplayers 4\n...
→ Saved directly as template.config text field
Add Startup Parameters: Edit Startup Parameters:
→ Enter param key (e.g., "+map") and value (e.g., "c1m1_hotel") → Multi-line text area for command line parameters
→ Renders as: +map c1m1_hotel in launch command → Enter raw format: +map c1m1_hotel\n+difficulty Hard\n...
→ Saved directly as template.startup_params text field
``` ```
### 4. Spawn Server from Template ### 4. Spawn Server from Template
@ -152,11 +152,10 @@ DELETE /server_templates/:id → ServerTemplatesController#destroy
POST /server_templates/:st_id/overlays/:id → OverlaysController#create POST /server_templates/:st_id/overlays/:id → OverlaysController#create
DELETE /server_templates/:st_id/overlays/:id → OverlaysController#destroy DELETE /server_templates/:st_id/overlays/:id → OverlaysController#destroy
POST /server_templates/:st_id/config_options → ConfigOptionsController#create GET /overlays → OverlaysController#index
DELETE /server_templates/:st_id/config_options/:id → ConfigOptionsController#destroy GET /overlays/new → OverlaysController#new
POST /overlays → OverlaysController#create
POST /server_templates/:st_id/startup_params → StartupParamsController#create DELETE /overlays/:id → OverlaysController#destroy
DELETE /server_templates/:st_id/startup_params/:id → StartupParamsController#destroy
GET /servers → ServersController#index GET /servers → ServersController#index
GET /servers/:id → ServersController#show GET /servers/:id → ServersController#show
@ -167,8 +166,6 @@ POST /servers/:id/stop → ServersController#stop
POST /servers/:id/restart → ServersController#restart POST /servers/:id/restart → ServersController#restart
DELETE /servers/:id → ServersController#destroy DELETE /servers/:id → ServersController#destroy
GET /overlays → OverlaysController#index
WS /cable → ActionCable (LogChannel) WS /cable → ActionCable (LogChannel)
``` ```
@ -183,16 +180,19 @@ WS /cable → ActionCable (LogChannel)
- `user_id` (references, nullable—system overlays have NULL user_id) - `user_id` (references, nullable—system overlays have NULL user_id)
- `name` (string, NOT NULL) - `name` (string, NOT NULL)
- `overlay_type` (string, NOT NULL, default: "system", enum: ["system", "custom"]) - `overlay_type` (string, NOT NULL, default: "system", enum: ["system", "custom"])
- `path` (string, NOT NULL) - `slug` (string, NOT NULL—POSIX-safe directory name, auto-derived from name)
- `description` (text) - `description` (text)
- Validates: name uniqueness per user, overlay_type inclusion - Validates: name uniqueness per user, overlay_type inclusion, slug is single directory (no slashes)
- Before validation: normalizes slug to lowercase, replaces special chars with underscores
- Scopes: system_overlays, custom_overlays, for_user - Scopes: system_overlays, custom_overlays, for_user
**ServerTemplate** **ServerTemplate**
- `user_id` (references, NOT NULL) - `user_id` (references, NOT NULL)
- `name` (string, NOT NULL) - `name` (string, NOT NULL)
- `config` (text, nullable—server.cfg contents)
- `startup_params` (text, nullable—command line parameters)
- Validates: name uniqueness per user - Validates: name uniqueness per user
- Has many: template_overlays → overlays, config_options, startup_params, servers - Has many: template_overlays → overlays, servers
**TemplateOverlay** (join table) **TemplateOverlay** (join table)
- `server_template_id` (references, NOT NULL) - `server_template_id` (references, NOT NULL)
@ -201,18 +201,6 @@ WS /cable → ActionCable (LogChannel)
- Validates: uniqueness of (server_template_id, overlay_id) and (server_template_id, position) - Validates: uniqueness of (server_template_id, overlay_id) and (server_template_id, position)
- Ordered by position ascending - Ordered by position ascending
**ConfigOption**
- `server_template_id` (references, NOT NULL)
- `config_key` (string, NOT NULL)
- `config_value` (string, NOT NULL)
- Validates: key uniqueness per template
**StartupParam**
- `server_template_id` (references, NOT NULL)
- `param_key` (string, NOT NULL)
- `param_value` (string, NOT NULL)
- Validates: key uniqueness per template
**Server** **Server**
- `user_id` (references, NOT NULL) - `user_id` (references, NOT NULL)
- `server_template_id` (references, NOT NULL) - `server_template_id` (references, NOT NULL)
@ -238,7 +226,7 @@ WS /cable → ActionCable (LogChannel)
**ConfigGenerator** **ConfigGenerator**
- `generate(server)` → Creates `/opt/l4d2/servers/{server_id}/server.cfg` - `generate(server)` → Creates `/opt/l4d2/servers/{server_id}/server.cfg`
- Iterates template.config_options, renders as `key "value"` format - Writes template.config text directly to server.cfg file
**Launcher** **Launcher**
- `spawn(server)` → Setup dirs, generate config, mount overlayfs, create systemd unit, start - `spawn(server)` → Setup dirs, generate config, mount overlayfs, create systemd unit, start

View file

@ -1,36 +0,0 @@
class ConfigOptionsController < ApplicationController
before_action :set_server_template
def create
@config_option = @server_template.config_options.build(config_option_params)
if @config_option.save
Activity.log(current_user, "added_config", "ServerTemplate", @server_template.id, { key: @config_option.config_key })
redirect_to @server_template, notice: "Config option added!"
else
redirect_to @server_template, alert: "Failed to add config option"
end
end
def destroy
@config_option = ConfigOption.find(params[:id])
authorize_user!
@config_option.destroy
Activity.log(current_user, "removed_config", "ServerTemplate", @server_template.id, { key: @config_option.config_key })
redirect_to @server_template, notice: "Config option deleted!"
end
private
def set_server_template
@server_template = current_user.server_templates.find(params[:server_template_id])
end
def config_option_params
params.require(:config_option).permit(:config_key, :config_value)
end
def authorize_user!
redirect_to dashboard_path, alert: "Not authorized" unless @server_template.user_id == current_user.id
end
end

View file

@ -54,7 +54,7 @@ class ServerTemplatesController < ApplicationController
end end
def server_template_params def server_template_params
params.require(:server_template).permit(:name) params.require(:server_template).permit(:name, :config, :startup_params)
end end
def authorize_user! def authorize_user!

View file

@ -1,36 +0,0 @@
class StartupParamsController < ApplicationController
before_action :set_server_template
def create
@startup_param = @server_template.startup_params.build(startup_param_params)
if @startup_param.save
Activity.log(current_user, "added_param", "ServerTemplate", @server_template.id, { key: @startup_param.param_key })
redirect_to @server_template, notice: "Startup parameter added!"
else
redirect_to @server_template, alert: "Failed to add parameter"
end
end
def destroy
@startup_param = StartupParam.find(params[:id])
authorize_user!
@startup_param.destroy
Activity.log(current_user, "removed_param", "ServerTemplate", @server_template.id, { key: @startup_param.param_key })
redirect_to @server_template, notice: "Startup parameter deleted!"
end
private
def set_server_template
@server_template = current_user.server_templates.find(params[:server_template_id])
end
def startup_param_params
params.require(:startup_param).permit(:param_key, :param_value)
end
def authorize_user!
redirect_to dashboard_path, alert: "Not authorized" unless @server_template.user_id == current_user.id
end
end

View file

@ -1,6 +0,0 @@
class ConfigOption < ApplicationRecord
belongs_to :server_template
validates :config_key, presence: true
validates :config_key, uniqueness: { scope: :server_template_id }
end

View file

@ -2,8 +2,6 @@ class ServerTemplate < ApplicationRecord
belongs_to :user belongs_to :user
has_many :template_overlays, dependent: :destroy has_many :template_overlays, dependent: :destroy
has_many :overlays, through: :template_overlays, source: :overlay has_many :overlays, through: :template_overlays, source: :overlay
has_many :config_options, dependent: :destroy
has_many :startup_params, dependent: :destroy
has_many :servers, dependent: :destroy has_many :servers, dependent: :destroy
validates :name, presence: true, uniqueness: { scope: :user_id } validates :name, presence: true, uniqueness: { scope: :user_id }

View file

@ -1,6 +0,0 @@
class StartupParam < ApplicationRecord
belongs_to :server_template
validates :param_key, presence: true
validates :param_key, uniqueness: { scope: :server_template_id }
end

View file

@ -8,16 +8,12 @@
tr tr
th Name th Name
th Overlays th Overlays
th Config Options
th Startup Params
th Actions th Actions
tbody tbody
- @server_templates.each do |template| - @server_templates.each do |template|
tr tr
td = link_to template.name, template td = link_to template.name, template
td = template.overlays.count td = template.overlays.count
td = template.config_options.count
td = template.startup_params.count
td td
= link_to "Edit", edit_server_template_path(template), class: "btn btn--small" = link_to "Edit", edit_server_template_path(template), class: "btn btn--small"
= link_to "Spawn", new_server_path(server_template_id: template.id), class: "btn btn--small btn--success" = link_to "Spawn", new_server_path(server_template_id: template.id), class: "btn btn--small btn--success"

View file

@ -24,64 +24,22 @@
- else - else
p No overlays selected. p No overlays selected.
section.config-options section.config
h3 Config Options h3 Server Config
- config_option = @server_template.config_options.build = form_with model: @server_template, url: server_template_path(@server_template), method: :patch, local: true do |f|
= form_with model: config_option, url: server_template_config_options_path(@server_template), method: :post, local: true do |f|
.form-group .form-group
= f.label :config_key, "Key" = f.label :config, "server.cfg contents"
= f.text_field :config_key, placeholder: "e.g., sv_pure" = f.text_area :config, placeholder: "sv_pure 2\nsv_maxplayers 4\n...", rows: 10
.form-group = f.submit "Save Config", class: "btn btn--small"
= f.label :config_value, "Value"
= f.text_field :config_value, placeholder: "e.g., 2"
= f.submit "Add Option", class: "btn btn--small"
- if @server_template.config_options.any?
table
thead
tr
th Key
th Value
th Actions
tbody
- @server_template.config_options.select(&:persisted?).each do |opt|
tr
td = opt.config_key
td = opt.config_value
td = link_to "Delete", server_template_config_option_path(@server_template, opt), method: :delete, data: { confirm: "Sure?" }, class: "btn btn--small btn--danger"
- else
p No config options yet.
section.startup-params section.startup-params
h3 Startup Parameters h3 Startup Parameters
- startup_param = @server_template.startup_params.build = form_with model: @server_template, url: server_template_path(@server_template), method: :patch, local: true do |f|
= form_with model: startup_param, url: server_template_startup_params_path(@server_template), method: :post, local: true do |f|
.form-group .form-group
= f.label :param_key, "Key" = f.label :startup_params, "Command line parameters"
= f.text_field :param_key, placeholder: "e.g., +map" = f.text_area :startup_params, placeholder: "+map c1m1_hotel\n+difficulty Hard\n...", rows: 10
.form-group = f.submit "Save Parameters", class: "btn btn--small"
= f.label :param_value, "Value"
= f.text_field :param_value, placeholder: "e.g., c1m1_hotel"
= f.submit "Add Parameter", class: "btn btn--small"
- if @server_template.startup_params.any?
table
thead
tr
th Key
th Value
th Actions
tbody
- @server_template.startup_params.select(&:persisted?).each do |param|
tr
td = param.param_key
td = param.param_value
td = link_to "Delete", server_template_startup_param_path(@server_template, param), method: :delete, data: { confirm: "Sure?" }, class: "btn btn--small btn--danger"
- else
p No startup parameters yet.
= link_to "Back", server_templates_path, class: "btn" = link_to "Back", server_templates_path, class: "btn"

View file

@ -36,20 +36,14 @@
p None p None
h4 Config Options h4 Config Options
- if @server_template.config_options.any? - if @server_template.config.present?
table pre = @server_template.config
- @server_template.config_options.each do |opt|
tr
td = opt.config_key
td = opt.config_value
- else - else
p None p None
h4 Startup Parameters h4 Startup Parameters
- if @server_template.startup_params.any? - if @server_template.startup_params.present?
ul pre = @server_template.startup_params
- @server_template.startup_params.each do |param|
li "#{param.param_key} #{param.param_value}"
- else - else
p None p None

View file

@ -13,8 +13,6 @@ Rails.application.routes.draw do
# Server resources # Server resources
resources :server_templates do resources :server_templates do
resources :overlays, only: [ :create, :destroy ] resources :overlays, only: [ :create, :destroy ]
resources :config_options, only: [ :create, :destroy ]
resources :startup_params, only: [ :create, :destroy ]
end end
resources :overlays, only: [ :index, :new, :create, :destroy ] resources :overlays, only: [ :index, :new, :create, :destroy ]

View file

@ -1,13 +0,0 @@
class CreateConfigOptions < ActiveRecord::Migration[8.1]
def change
create_table :config_options do |t|
t.references :server_template, null: false, foreign_key: true
t.string :config_key, null: false
t.string :config_value, null: false
t.timestamps
end
add_index :config_options, [ :server_template_id, :config_key ], unique: true
end
end

View file

@ -1,13 +0,0 @@
class CreateStartupParams < ActiveRecord::Migration[8.1]
def change
create_table :startup_params do |t|
t.references :server_template, null: false, foreign_key: true
t.string :param_key, null: false
t.string :param_value, null: false
t.timestamps
end
add_index :startup_params, [ :server_template_id, :param_key ], unique: true
end
end

View file

@ -0,0 +1,6 @@
class AddConfigAndStartupParamsToServerTemplates < ActiveRecord::Migration[8.1]
def change
add_column :server_templates, :config, :text
add_column :server_templates, :startup_params, :text
end
end

View file

@ -0,0 +1,6 @@
class DropConfigOptionsAndStartupParamsTables < ActiveRecord::Migration[8.1]
def change
drop_table :config_options
drop_table :startup_params
end
end

26
db/schema.rb generated
View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.1].define(version: 2026_01_18_180000) do ActiveRecord::Schema[8.1].define(version: 2026_01_18_200000) do
create_table "activities", force: :cascade do |t| create_table "activities", force: :cascade do |t|
t.string "action", null: false t.string "action", null: false
t.datetime "created_at", null: false t.datetime "created_at", null: false
@ -23,16 +23,6 @@ ActiveRecord::Schema[8.1].define(version: 2026_01_18_180000) do
t.index ["user_id"], name: "index_activities_on_user_id" t.index ["user_id"], name: "index_activities_on_user_id"
end end
create_table "config_options", force: :cascade do |t|
t.string "config_key", null: false
t.string "config_value", null: false
t.datetime "created_at", null: false
t.integer "server_template_id", null: false
t.datetime "updated_at", null: false
t.index ["server_template_id", "config_key"], name: "index_config_options_on_server_template_id_and_config_key", unique: true
t.index ["server_template_id"], name: "index_config_options_on_server_template_id"
end
create_table "overlays", force: :cascade do |t| create_table "overlays", force: :cascade do |t|
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.text "description" t.text "description"
@ -46,8 +36,10 @@ ActiveRecord::Schema[8.1].define(version: 2026_01_18_180000) do
end end
create_table "server_templates", force: :cascade do |t| create_table "server_templates", force: :cascade do |t|
t.text "config"
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.string "name", null: false t.string "name", null: false
t.text "startup_params"
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.integer "user_id", null: false t.integer "user_id", null: false
t.index ["user_id", "name"], name: "index_server_templates_on_user_id_and_name", unique: true t.index ["user_id", "name"], name: "index_server_templates_on_user_id_and_name", unique: true
@ -71,16 +63,6 @@ ActiveRecord::Schema[8.1].define(version: 2026_01_18_180000) do
t.index ["user_id"], name: "index_servers_on_user_id" t.index ["user_id"], name: "index_servers_on_user_id"
end end
create_table "startup_params", force: :cascade do |t|
t.datetime "created_at", null: false
t.string "param_key", null: false
t.string "param_value", null: false
t.integer "server_template_id", null: false
t.datetime "updated_at", null: false
t.index ["server_template_id", "param_key"], name: "index_startup_params_on_server_template_id_and_param_key", unique: true
t.index ["server_template_id"], name: "index_startup_params_on_server_template_id"
end
create_table "template_overlays", force: :cascade do |t| create_table "template_overlays", force: :cascade do |t|
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.integer "overlay_id", null: false t.integer "overlay_id", null: false
@ -102,12 +84,10 @@ ActiveRecord::Schema[8.1].define(version: 2026_01_18_180000) do
end end
add_foreign_key "activities", "users" add_foreign_key "activities", "users"
add_foreign_key "config_options", "server_templates"
add_foreign_key "overlays", "users" add_foreign_key "overlays", "users"
add_foreign_key "server_templates", "users" add_foreign_key "server_templates", "users"
add_foreign_key "servers", "server_templates" add_foreign_key "servers", "server_templates"
add_foreign_key "servers", "users" add_foreign_key "servers", "users"
add_foreign_key "startup_params", "server_templates"
add_foreign_key "template_overlays", "overlays" add_foreign_key "template_overlays", "overlays"
add_foreign_key "template_overlays", "server_templates" add_foreign_key "template_overlays", "server_templates"
end end