diff --git a/CLAUDE.md b/CLAUDE.md
index 26698ef..96bbcee 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -24,6 +24,7 @@ ShopDB is a Classic ASP/VBScript web application for managing manufacturing shop
- Production IIS logs
- Production MySQL database
- Zabbix server (10.48.130.113)
+- Employee database (wjf_employees) - used by displayprofile.asp (can update code, cannot test)
For production tasks, user must:
- Relay production data/logs to Claude
diff --git a/api.asp b/api.asp
index f981ecd..018bfba 100644
--- a/api.asp
+++ b/api.asp
@@ -1,21 +1,13 @@
<%@ Language=VBScript %>
+
<%
' ============================================================================
-' DATABASE CONNECTION - Created directly in api.asp to avoid scoping issues
+' DATABASE CONNECTION - Uses centralized config.asp
' ============================================================================
-Dim objConn, rs, DB_CONN_STRING
-' Use direct MySQL ODBC driver connection (same as sql.asp) instead of DSN
-DB_CONN_STRING = "Driver={MySQL ODBC 9.4 Unicode Driver};" & _
- "Server=192.168.122.1;" & _
- "Port=3306;" & _
- "Database=shopdb;" & _
- "User=570005354;" & _
- "Password=570005354;" & _
- "Option=3;" & _
- "Pooling=True;Max Pool Size=100;"
-Session.Timeout = 15
+Dim objConn, rs
+Session.Timeout = APP_SESSION_TIMEOUT
Set objConn = Server.CreateObject("ADODB.Connection")
-objConn.ConnectionString = DB_CONN_STRING
+objConn.ConnectionString = GetConnectionString()
objConn.Open
Set rs = Server.CreateObject("ADODB.Recordset")
diff --git a/apiusb.asp b/apiusb.asp
index 205efbe..5e875a8 100644
--- a/apiusb.asp
+++ b/apiusb.asp
@@ -1,30 +1,25 @@
<%@ Language="VBScript" %>
<%
+Option Explicit
+%>
+
+<%
'=============================================================================
' FILE: apiusb.asp
' PURPOSE: API endpoints for USB device operations
' SECURITY: Parameterized queries, JSON output
' CREATED: 2025-12-07
'=============================================================================
-Option Explicit
Response.ContentType = "application/json"
Response.Charset = "utf-8"
Response.Buffer = True
-' Create database connection directly (avoid sql.asp scoping issues)
-Dim objConn, DB_CONN_STRING
-DB_CONN_STRING = "Driver={MySQL ODBC 9.4 Unicode Driver};" & _
- "Server=192.168.122.1;" & _
- "Port=3306;" & _
- "Database=shopdb;" & _
- "User=570005354;" & _
- "Password=570005354;" & _
- "Option=3;" & _
- "Pooling=True;Max Pool Size=100;"
+' Database connection using centralized config
+Dim objConn
On Error Resume Next
Set objConn = Server.CreateObject("ADODB.Connection")
-objConn.ConnectionString = DB_CONN_STRING
+objConn.ConnectionString = GetConnectionString()
objConn.Open
If Err.Number <> 0 Then
diff --git a/displayprofile.asp b/displayprofile.asp
index 010c061..06a8659 100644
--- a/displayprofile.asp
+++ b/displayprofile.asp
@@ -7,6 +7,7 @@ Option Explicit
+
@@ -280,15 +281,13 @@ END IF
USB Checkout History
<%
-' Connect to shopdb for USB history
+' Connect to shopdb for USB history (uses centralized config)
Dim objConnShopdb, shopdbAvailable
shopdbAvailable = False
On Error Resume Next
Set objConnShopdb = Server.CreateObject("ADODB.Connection")
-objConnShopdb.ConnectionString = "Driver={MySQL ODBC 9.4 Unicode Driver};" & _
- "Server=192.168.122.1;Port=3306;Database=shopdb;" & _
- "User=570005354;Password=570005354;Option=3;"
+objConnShopdb.ConnectionString = GetConnectionString()
objConnShopdb.Open
If Err.Number = 0 Then
shopdbAvailable = True
diff --git a/includes/config.asp b/includes/config.asp
index 31a1cb8..c0d3920 100644
--- a/includes/config.asp
+++ b/includes/config.asp
@@ -10,8 +10,17 @@
'=============================================================================
'-----------------------------------------------------------------------------
-' Database Configuration
+' Database Configuration - ShopDB (primary)
'-----------------------------------------------------------------------------
+' Set USE_DSN = True for production (DSN-based), False for dev (direct ODBC)
+Const USE_DSN = False
+
+' DSN configuration (production)
+Const DB_DSN = "shopdb"
+Const DB_DSN_USER = "570005354"
+Const DB_DSN_PASSWORD = "570005354"
+
+' Direct ODBC configuration (development)
Const DB_DRIVER = "MySQL ODBC 9.4 Unicode Driver"
Const DB_SERVER = "192.168.122.1"
Const DB_PORT = "3306"
@@ -19,6 +28,25 @@ Const DB_NAME = "shopdb"
Const DB_USER = "570005354"
Const DB_PASSWORD = "570005354"
+'-----------------------------------------------------------------------------
+' Database Configuration - Employee Database
+'-----------------------------------------------------------------------------
+' Set USE_EMP_DSN = True for production (DSN-based), False for dev (direct ODBC)
+Const USE_EMP_DSN = True
+
+' DSN configuration (production)
+Const EMP_DB_DSN = "wjf_employees"
+Const EMP_DB_DSN_USER = "root"
+Const EMP_DB_DSN_PASSWORD = "WJF11sql"
+
+' Direct ODBC configuration (development) - configure if needed
+Const EMP_DB_DRIVER = "MySQL ODBC 9.4 Unicode Driver"
+Const EMP_DB_SERVER = "localhost"
+Const EMP_DB_PORT = "3306"
+Const EMP_DB_NAME = "wjf_employees"
+Const EMP_DB_USER = "root"
+Const EMP_DB_PASSWORD = "WJF11sql"
+
'-----------------------------------------------------------------------------
' Application Settings
'-----------------------------------------------------------------------------
@@ -42,11 +70,17 @@ Const DEFAULT_MODEL_ID = 1 ' Default model
Const DEFAULT_OS_ID = 1 ' Default operating system
'-----------------------------------------------------------------------------
-' External Services
+' External Services - ServiceNow
'-----------------------------------------------------------------------------
Const SNOW_BASE_URL = "https://geit.service-now.com/now/nav/ui/search/"
Const SNOW_TICKET_PREFIXES = "geinc,gechg,gerit,gesct" ' Valid ServiceNow ticket prefixes
+'-----------------------------------------------------------------------------
+' External Services - Zabbix API
+'-----------------------------------------------------------------------------
+Const ZABBIX_URL = "http://10.48.130.113:8080/api_jsonrpc.php"
+Const ZABBIX_API_TOKEN = "9e60b0544ec77131d94825eaa2f3f1645335539361fd33644aeb8326697aa48d"
+
'-----------------------------------------------------------------------------
' File Upload
'-----------------------------------------------------------------------------
@@ -59,18 +93,45 @@ Const ALLOWED_EXTENSIONS = "jpg,jpeg,png,gif,pdf"
'-----------------------------------------------------------------------------
' FUNCTION: GetConnectionString
-' PURPOSE: Returns the database connection string with all parameters
-' RETURNS: Complete ODBC connection string
+' PURPOSE: Returns the database connection string based on USE_DSN setting
+' RETURNS: DSN connection string (production) or direct ODBC string (dev)
'-----------------------------------------------------------------------------
Function GetConnectionString()
- GetConnectionString = "Driver={" & DB_DRIVER & "};" & _
- "Server=" & DB_SERVER & ";" & _
- "Port=" & DB_PORT & ";" & _
- "Database=" & DB_NAME & ";" & _
- "User=" & DB_USER & ";" & _
- "Password=" & DB_PASSWORD & ";" & _
- "Option=3;" & _
- "Pooling=True;Max Pool Size=100;"
+ If USE_DSN Then
+ ' Production: DSN-based connection with pooling
+ GetConnectionString = "DSN=" & DB_DSN & ";Uid=" & DB_DSN_USER & ";Pwd=" & DB_DSN_PASSWORD & ";Option=3;Pooling=True;Max Pool Size=100;"
+ Else
+ ' Development: Direct ODBC driver connection
+ GetConnectionString = "Driver={" & DB_DRIVER & "};" & _
+ "Server=" & DB_SERVER & ";" & _
+ "Port=" & DB_PORT & ";" & _
+ "Database=" & DB_NAME & ";" & _
+ "User=" & DB_USER & ";" & _
+ "Password=" & DB_PASSWORD & ";" & _
+ "Option=3;" & _
+ "Pooling=True;Max Pool Size=100;"
+ End If
+End Function
+
+'-----------------------------------------------------------------------------
+' FUNCTION: GetEmployeeConnectionString
+' PURPOSE: Returns the employee database connection string based on USE_EMP_DSN
+' RETURNS: DSN connection string (production) or direct ODBC string (dev)
+'-----------------------------------------------------------------------------
+Function GetEmployeeConnectionString()
+ If USE_EMP_DSN Then
+ ' Production: DSN-based connection
+ GetEmployeeConnectionString = "DSN=" & EMP_DB_DSN & ";Uid=" & EMP_DB_DSN_USER & ";Pwd=" & EMP_DB_DSN_PASSWORD
+ Else
+ ' Development: Direct ODBC driver connection
+ GetEmployeeConnectionString = "Driver={" & EMP_DB_DRIVER & "};" & _
+ "Server=" & EMP_DB_SERVER & ";" & _
+ "Port=" & EMP_DB_PORT & ";" & _
+ "Database=" & EMP_DB_NAME & ";" & _
+ "User=" & EMP_DB_USER & ";" & _
+ "Password=" & EMP_DB_PASSWORD & ";" & _
+ "Option=3;"
+ End If
End Function
'-----------------------------------------------------------------------------
diff --git a/includes/sql.asp b/includes/sql.asp
index 7e6da7f..5ef4dc0 100644
--- a/includes/sql.asp
+++ b/includes/sql.asp
@@ -1,18 +1,9 @@
+
<%
' objConn - script-global connection object (no Dim for global scope)
- Session.Timeout=15
- Set objConn=Server.CreateObject("ADODB.Connection")
- ' Old DSN connection:
- ' objConn.ConnectionString="DSN=shopdb;Uid=root;Pwd=WJF11sql"
- ' Direct MySQL ODBC connection with pooling enabled:
- objConn.ConnectionString="Driver={MySQL ODBC 9.4 Unicode Driver};" & _
- "Server=192.168.122.1;" & _
- "Port=3306;" & _
- "Database=shopdb;" & _
- "User=570005354;" & _
- "Password=570005354;" & _
- "Option=3;" & _
- "Pooling=True;Max Pool Size=100;"
+ Session.Timeout = APP_SESSION_TIMEOUT
+ Set objConn = Server.CreateObject("ADODB.Connection")
+ objConn.ConnectionString = GetConnectionString()
objConn.Open
- set rs = server.createobject("ADODB.Recordset")
+ Set rs = Server.CreateObject("ADODB.Recordset")
%>
\ No newline at end of file
diff --git a/includes/wjf_employees-sql.asp b/includes/wjf_employees-sql.asp
index 624a09a..7113eaa 100644
--- a/includes/wjf_employees-sql.asp
+++ b/includes/wjf_employees-sql.asp
@@ -1,8 +1,10 @@
+
<%
+ ' Employee database connection - uses centralized config
Dim objConn
- Session.Timeout=15
- Set objConn=Server.CreateObject("ADODB.Connection")
- objConn.ConnectionString="DSN=wjf_employees;Uid=root;Pwd=WJF11sql"
- objConn.Open
- set rs = server.createobject("ADODB.Recordset")
+ Session.Timeout = APP_SESSION_TIMEOUT
+ Set objConn = Server.CreateObject("ADODB.Connection")
+ objConn.ConnectionString = GetEmployeeConnectionString()
+ objConn.Open
+ Set rs = Server.CreateObject("ADODB.Recordset")
%>
\ No newline at end of file
diff --git a/includes/zabbix.asp b/includes/zabbix.asp
index 1999e17..7dbdd2c 100644
--- a/includes/zabbix.asp
+++ b/includes/zabbix.asp
@@ -1,7 +1,6 @@
+
<%
-' Zabbix API Configuration
-Const ZABBIX_URL = "http://10.48.130.113:8080/api_jsonrpc.php"
-Const ZABBIX_API_TOKEN = "9e60b0544ec77131d94825eaa2f3f1645335539361fd33644aeb8326697aa48d"
+' Zabbix API Configuration - uses ZABBIX_URL and ZABBIX_API_TOKEN from config.asp
' Function to make HTTP POST request to Zabbix API with Bearer token
Function ZabbixAPICall(jsonRequest)
diff --git a/sql/migration_cleanup_pc_types.sql b/sql/migration_cleanup_pc_types.sql
index b34eb43..8a67794 100644
--- a/sql/migration_cleanup_pc_types.sql
+++ b/sql/migration_cleanup_pc_types.sql
@@ -5,10 +5,13 @@
-- ============================================================================
--
-- CHANGES:
--- 1. Add primary key to installedapps table
--- 2. Migrate machines using PC-specific machinetypes to generic PC (33) + pctypeid
--- 3. Update models to use generic PC machinetype
--- 4. Remove unused PC machinetypes (34-43, 45-46), keep USB Device (44)
+-- 1. Drop duplicate appname_2 index on applications table
+-- 2. Add primary key to installedapps table (if not exists)
+-- 3. Migrate machines using PC-specific machinetypes to generic PC (33) + pctypeid
+-- 4. Update models to use generic PC machinetype
+-- 5. Remove unused PC machinetypes (34-43, 45-46), keep USB Device (44)
+--
+-- NOTE: This migration is IDEMPOTENT - safe to run multiple times
--
-- RUN ON: Production database
-- BACKUP FIRST: mysqldump -u root -p shopdb > shopdb_backup_$(date +%Y%m%d).sql
@@ -18,16 +21,42 @@
START TRANSACTION;
-- ============================================================================
--- 1. ADD PRIMARY KEY TO INSTALLEDAPPS TABLE
+-- 1. DROP DUPLICATE INDEX ON APPLICATIONS
-- ============================================================================
-ALTER TABLE installedapps
-ADD COLUMN installedappid INT AUTO_INCREMENT PRIMARY KEY FIRST;
+-- Check if appname_2 index exists before dropping
+SET @index_exists = (SELECT COUNT(*) FROM information_schema.STATISTICS
+ WHERE table_schema = DATABASE()
+ AND table_name = 'applications'
+ AND index_name = 'appname_2');
-SELECT 'Added PK to installedapps' AS status;
+SET @sql = IF(@index_exists > 0, 'DROP INDEX appname_2 ON applications', 'SELECT "Index appname_2 does not exist" AS status');
+PREPARE stmt FROM @sql;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SELECT IF(@index_exists > 0, 'Dropped duplicate appname_2 index', 'Index appname_2 already removed') AS status;
-- ============================================================================
--- 2. MIGRATE MACHINES FROM PC-SPECIFIC TYPES TO GENERIC PC (33) + PCTYPEID
+-- 2. ADD PRIMARY KEY TO INSTALLEDAPPS TABLE (if not exists)
-- ============================================================================
+SET @col_exists = (SELECT COUNT(*) FROM information_schema.COLUMNS
+ WHERE table_schema = DATABASE()
+ AND table_name = 'installedapps'
+ AND column_name = 'installedappid');
+
+SET @sql = IF(@col_exists = 0,
+ 'ALTER TABLE installedapps ADD COLUMN installedappid INT AUTO_INCREMENT PRIMARY KEY FIRST',
+ 'SELECT "Column installedappid already exists" AS status');
+PREPARE stmt FROM @sql;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SELECT IF(@col_exists = 0, 'Added PK to installedapps', 'PK installedappid already exists') AS status;
+
+-- ============================================================================
+-- 3. MIGRATE MACHINES FROM PC-SPECIFIC TYPES TO GENERIC PC (33) + PCTYPEID
+-- ============================================================================
+-- These UPDATEs are already idempotent (won't change rows that don't match)
-- PC - Standard (36) → machinetypeid=33, pctypeid=1 (Standard)
UPDATE machines
@@ -64,11 +93,11 @@ UPDATE machines
SET machinetypeid = 33, pctypeid = 4
WHERE machinetypeid BETWEEN 34 AND 46 AND pctypeid IS NULL;
-SELECT CONCAT('Total machines now using machinetypeid 34-46: ',
+SELECT CONCAT('Total machines still using machinetypeid 34-46: ',
(SELECT COUNT(*) FROM machines WHERE machinetypeid BETWEEN 34 AND 46)) AS status;
-- ============================================================================
--- 3. UPDATE MODELS TO USE GENERIC PC MACHINETYPE (33)
+-- 4. UPDATE MODELS TO USE GENERIC PC MACHINETYPE (33)
-- ============================================================================
UPDATE models
SET machinetypeid = 33
@@ -77,9 +106,10 @@ WHERE machinetypeid BETWEEN 34 AND 46;
SELECT CONCAT('Updated ', ROW_COUNT(), ' models to generic PC type') AS status;
-- ============================================================================
--- 4. DELETE REDUNDANT MACHINETYPES
+-- 5. DELETE REDUNDANT MACHINETYPES
-- ============================================================================
-- Keep 33 (PC) and 44 (USB Device), remove 34-43 and 45-46
+-- These DELETEs are already idempotent (won't delete if rows don't exist)
DELETE FROM machinetypes WHERE machinetypeid BETWEEN 34 AND 43;
SELECT CONCAT('Deleted ', ROW_COUNT(), ' machinetypes (34-43)') AS status;
@@ -101,10 +131,13 @@ WHERE m.pctypeid IS NOT NULL
GROUP BY m.pctypeid
ORDER BY count DESC;
+SELECT 'VERIFICATION - Applications indexes:' AS info;
+SELECT index_name, column_name FROM information_schema.STATISTICS
+WHERE table_schema = DATABASE() AND table_name = 'applications';
+
-- ============================================================================
--- COMMIT (uncomment when ready to apply)
+-- COMMIT
-- ============================================================================
COMMIT;
--- ROLLBACK; -- Use this instead if something looks wrong
SELECT 'Migration completed successfully!' AS status;