A comprehensive web application for managing Counter-Strike 2 weapon loadouts, built with Nuxt 3 and modern web technologies.
CS2 Loadout Manager is a full-stack web application that provides Counter-Strike 2 players with a comprehensive tool for creating, managing, and customizing their weapon loadouts. The application features Steam authentication, real-time data synchronization with CS2 APIs, and an intuitive interface for weapon customization.
- Multi-Loadout Management: Create and manage multiple weapon loadouts
- Comprehensive Customization: Configure weapons, knives, gloves, agents, music kits, and pins
- Team-Specific Equipment: Separate configurations for Terrorist and Counter-Terrorist sides
- Steam Integration: Secure authentication via Steam OpenID
- Real-time Data: Automatic synchronization with CS2 item databases
- Inspect Link Support: Import items directly from CS2 inspect links
- Internationalization: Multi-language support (English, German, Russian)
- Responsive Design: Optimized for desktop and mobile devices
- Advanced Customization: Sticker positioning, wear values, StatTrak counters, and name tags
- Nuxt 3 - Vue.js framework with SSR/SSG capabilities
- Vue 3 - Progressive JavaScript framework with Composition API
- TypeScript - Type-safe JavaScript development
- Tailwind CSS - Utility-first CSS framework
- Sass - CSS preprocessor for advanced styling
- Naive UI - Vue 3 component library
- Pinia - State management for Vue
- VueUse - Collection of Vue composition utilities
- Iconify - Unified icon framework
- Nitro - Universal web server (built into Nuxt 3)
- H3 - Minimal HTTP framework
- MariaDB - Relational database management system
- JWT - JSON Web Tokens for authentication
- Steam OpenID - Steam authentication integration
- Vite - Fast build tool and development server
- ESLint - JavaScript/TypeScript linting
- Vitest - Unit testing framework
- Playwright - End-to-end testing
- Bun - JavaScript runtime and package manager
- CS:GO API - CS2 item data and images
- Steam Web API - Steam user data and authentication
- GlobalOffensive Node.js - CS2 game coordinator integration
- Axios - HTTP client for API requests
- Node Cache - In-memory caching
- Auto-animate - Smooth animations
- Class Variance Authority - CSS class management
- Date-fns - Date utility library
cs2inspect-web/
βββ π assets/ # Static assets (CSS, JS, SVG)
β βββ css/ # Stylesheets (Sass/CSS)
β βββ js/ # JavaScript utilities
β βββ svg/ # SVG icons and graphics
βββ π components/ # Vue components
β βββ AgentTabs.vue # Agent selection interface
β βββ *SkinModal.vue # Weapon/knife/glove customization modals
β βββ *Tabs.vue # Tab navigation components
β βββ ... # Other reusable components
βββ π composables/ # Vue composables
β βββ useInspectItem.ts # Inspect link handling
β βββ useItems.ts # Item management utilities
βββ π layouts/ # Nuxt layouts
β βββ default.vue # Main application layout
βββ π locales/ # Internationalization files
β βββ en.json # English translations
β βββ de.json # German translations
β βββ ru.json # Russian translations
βββ π middleware/ # Route middleware
β βββ validate-weapon-url.ts # URL validation
βββ π pages/ # Nuxt pages (file-based routing)
β βββ agents/ # Agent selection page
β βββ auth/ # Authentication pages
β βββ gloves/ # Glove customization
β βββ knifes/ # Knife customization
β βββ music-kits/ # Music kit selection
β βββ pins/ # Pin selection
β βββ weapons/ # Weapon customization
β βββ index.vue # Home page
βββ π server/ # Server-side code
β βββ api/ # API endpoints
β βββ database/ # Database configuration and schemas
β βββ middleware/ # Server middleware (auth, CORS, etc.)
β βββ plugins/ # Server plugins (initialization)
β βββ utils/ # Server utilities and constants
βββ π services/ # Service layer
β βββ steamAuth.ts # Steam authentication service
βββ π storage/ # File storage
β βββ csgo-api/ # Cached CS2 API data
βββ π stores/ # Pinia stores
β βββ loadoutStore.ts # Loadout state management
βββ π utils/ # Client-side utilities
β βββ colors.ts # Color utilities
β βββ menuConfig.ts # Menu configuration
βββ nuxt.config.ts # Nuxt configuration
βββ package.json # Dependencies and scripts
βββ tailwind.config.ts # Tailwind CSS configuration
βββ tsconfig.json # TypeScript configuration
- Initialization: Fetches latest CS2 item data from the CSGO-API
- Authentication: Handles Steam authentication and user sessions
- Database: Manages user loadouts and weapon configurations
- Authentication: User logs in via Steam
- Loadout Management:
- Fetches user loadouts from
/api/loadouts?steamid=[id]
- Displays loadout selection interface
- Fetches user loadouts from
- Weapon Customization:
- Fetches weapon data for selected loadout via
/api/weapons/[type]?loadoutid=[id]&steamid=[id]
- Provides interface for skin, sticker, and keychain selection
- Fetches weapon data for selected loadout via
Represents weapon skins from the CS2 API:
interface APISkin {
id: string;
name: string;
description?: string;
weapon: {
id: string;
name: string;
weapon_id: string;
}
category: {
id: string;
name: string
}
pattern: {
id: string;
name: string;
}
min_float: number;
max_float: number;
rarity: {
id: string;
name: string;
color: string;
}
stattrak?: boolean
souvenir?: boolean
paint_index: string
wears?: any[]
collections?: any[]
crates?: any[]
team?: {
id: string;
name: string;
}
image: string;
}
Represents stickers from the CS2 API:
interface APISticker {
id: string;
name: string;
description?: string;
rarity: {
id: string;
name: string;
color: string
}
crates?: any[]
tournament_event: string
tournament_team: string
type: string
market_hash_name?: string
effect?: string
image: string
}
Represents weapon charms from the CS2 API:
interface APIKeychain {
id: string;
name: string;
description?: string;
rarity: {
id: string;
name: string;
color: string;
}
market_hash_name?: string;
image: string;
}
Represents music kits from the CS2 API:
interface APIMusicKit {
id: string
name: string
description?: string
rarity: {
id: string
name: string
color: string
}
market_hash_name?: string
exclusive?: boolean
image: string
}
Represents player models from the CS2 API:
interface APIAgent {
id: string;
name: string;
description?: string;
rarity: {
id: string;
name: string;
color: string;
}
collections?: any[];
team: {
id: string;
name: string;
}
market_hash_name?: string;
image: string;
}
Represents a user's loadout in the database:
interface DBLoadout {
id: string;
steamid: string;
name: string;
selected_knife_t: number | null;
selected_knife_ct: number | null;
selected_glove_t: number | null;
selected_glove_ct: number | null;
created_at: string;
updated_at: string;
}
Represents a user's knife entry in the database:
interface DBKnife {
id: string;
steamid: string;
loadoutid: string;
active: boolean;
team: number;
defindex: number;
paintindex: number;
paintseed: string;
paintwear: string;
stattrak_enabled: boolean;
stattrak_count: number;
nametag: string;
created_at: string;
updated_at: string;
}
Represents a user's glove entry in the database:
interface DBGlove {
id: string;
steamid: string;
loadoutid: string;
active: boolean;
team: number;
defindex: number;
paintindex: number;
paintseed: string;
paintwear: string;
created_at: string;
updated_at: string;
}
Represents a user's weapon entry in the database:
interface DBWeapon {
id: string;
steamid: string;
loadoutid: string;
active: boolean;
team: number;
defindex: number;
paintindex: number;
paintseed: string;
paintwear: string;
stattrak_enabled: boolean;
stattrak_count: number;
nametag: string;
sticker_0: string;
sticker_1: string;
sticker_2: string;
sticker_3: string;
sticker_4: string;
keychain: string;
created_at: string;
updated_at: string;
}
Represents a user's pin entry in the database:
interface DBPin {
id: string;
steamid: string;
loadoutid: string;
active: boolean
team: number
pinid: number;
created_at: string;
updated_at: string;
}
Represents a user's music kit entry in the database:
interface DBMusicKit {
id: string;
steamid: string;
loadoutid: string;
active: boolean
team: number;
musicid: number;
created_at: string;
updated_at: string;
}
Represents a user's agent entry in the database:
interface DBAgent {
id: string;
steamid: string;
loadoutid: string;
active: boolean
team: number;
defindex: number;
agent_name: string;
created_at: string;
updated_at: string;
}
Represents a sticker applied to a weapon:
interface IEnhancedWeaponSticker {
id: number;
x: number;
y: number;
wear: number;
scale: number;
rotation: number;
api: {
name: string;
type: string;
image: string;
effect: string;
rarity: {
name: string;
id: string;
color: string;
};
tournament_team: string;
tournament_event: string;
};
}
Represents a keychain attached to a weapon:
interface IEnhancedWeaponKeychain {
id: number;
x: number;
y: number;
z: number;
seed: number;
api: {
name: string;
image: string;
rarity: {
name: string;
id: string;
color: string;
};
};
}
IMappedDBWeapon
represents a weapon's database information with mapped Keychain and Sticker data:
interface IMappedDBWeapon {
active: boolean;
team: number;
defindex: number;
statTrak: boolean;
statTrakCount: number;
paintIndex: number;
paintWear: number;
pattern: number;
nameTag: string;
stickers: any;
keychain: IEnhancedWeaponKeychain | null;
}
IDefaultItem
represents default weapon information used from /server/utils/constants.ts
to display default weapons
interface IDefaultItem {
weapon_defindex: number;
defaultName: string;
paintIndex: number;
defaultImage: string;
weapon_name: string;
category: string;
availableTeams: string;
}
IEnhancedItem
extends IDefaultItem
and uses DBKnife
or IMappedDBWeapon
or DBGlove
for database information:
interface IEnhancedItem extends IDefaultItem {
name: string;
image: string;
minFloat: number;
maxFloat: number;
rarity?: {
name: string;
id: string;
color: string;
};
databaseInfo?: DBKnife | IEnha | IMappedDBWeapon;
}
The application uses the following database tables:
Table | Description |
---|---|
wp_player_loadouts |
Stores user loadout configurations |
wp_player_knifes |
Knife configurations for each loadout |
wp_player_gloves |
Glove configurations for each loadout |
wp_player_rifles |
Rifle configurations for each loadout |
wp_player_smgs |
SMG configurations for each loadout |
wp_player_pistols |
Pistol configurations for each loadout |
wp_player_heavys |
Heavy weapon configurations for each loadout |
wp_player_agents |
Agent (player model) configurations |
wp_player_pins |
Pin configurations |
wp_player_music |
Music kit configurations |
All endpoints require authentication.
Fetches all loadouts for a user.
Response:
{
"loadouts": [
{
"id": 1,
"steamid": "1234567890",
"name": "Example Name",
"selected_knife_t": 500,
"selected_knife_ct": 505,
"selected_glove_t": null,
"selected_glove_ct": null,
"created_at": "2025-01-30T15:48:11.000Z",
"updated_at": "2025-03-15T01:20:21.000Z"
}
],
"message": "Loadouts fetched successfully!"
}
Creates a new loadout.
Request:
{
"name": "Example Name"
}
Response:
{
"loadout": {
"id": 1,
"steamid": "1234567890",
"name": "Example Name",
"selected_knife_t": 500,
"selected_knife_ct": 505,
"selected_glove_t": null,
"selected_glove_ct": null,
"created_at": "2025-01-30T15:48:11.000Z",
"updated_at": "2025-03-15T01:20:21.000Z"
},
"message": "Loadout created successfully"
}
Updates a loadout (currently only name updates are supported).
Request:
{
"name": "New Name"
}
Response:
{
"loadout": {
"id": 1,
"steamid": "1234567890",
"name": "New Name",
"selected_knife_t": 500,
"selected_knife_ct": 505,
"selected_glove_t": null,
"selected_glove_ct": null,
"created_at": "2025-01-30T15:48:11.000Z",
"updated_at": "2025-03-15T01:20:21.000Z"
},
"message": "Loadout updated successfully"
}
Deletes a loadout.
Response:
{
"message": "Loadout deleted successfully"
}
Updates the selected knife/glove for a loadout.
Request:
{
"team": 1, // 1 = Terrorists, 2 = Counter Terrorists
"defindex": 503 // Weapon defindex, or null to unselect
}
Fetches all knives for a specific loadout.
Response:
{
"knifes": [
{
"weapon_defindex": 500,
"defaultName": "Bayonet",
"paintIndex": 38,
"defaultImage": "https://example.com/bayonet.png",
"weapon_name": "Bayonet",
"category": "knife",
"availableTeams": "both",
"name": "Bayonet | Fade",
"image": "https://example.com/bayonet_fade.png",
"minFloat": 0.01,
"maxFloat": 0.08,
"rarity": {
"name": "Covert",
"id": "covert",
"color": "#eb4b4b"
},
"databaseInfo": {
"active": true,
"team": 1,
"defindex": 500,
"paintIndex": 38,
"pattern": 661,
"wear": 0.01,
"statTrak": true,
"statTrakCount": 1337,
"nameTag": "My Knife"
}
}
]
}
Saves or updates a knife configuration.
Request:
{
"defindex": 500,
"active": true,
"paintIndex": 38,
"wear": 0.01,
"pattern": 661,
"statTrak": true,
"statTrakCount": 1337,
"nameTag": "My Knife",
"team": 1,
"reset": false
}
Response:
{
"success": true,
"message": "Knifes updated successfully"
}
Fetches all gloves for a specific loadout.
Response:
{
"knifes": [
{
"weapon_defindex": 5027,
"defaultName": "Specialist Gloves",
"paintIndex": 10006,
"defaultImage": "https://example.com/specialist_gloves.png",
"weapon_name": "Specialist Gloves",
"category": "glove",
"availableTeams": "both",
"name": "Specialist Gloves | Crimson Kimono",
"image": "https://example.com/specialist_gloves_crimson.png",
"minFloat": 0.06,
"maxFloat": 0.8,
"rarity": {
"name": "Extraordinary",
"id": "extraordinary",
"color": "#eb4b4b"
},
"databaseInfo": {
"active": true,
"team": 1,
"defindex": 5027,
"paintIndex": 10006,
"pattern": 661,
"wear": 0.15
}
}
]
}
Saves or updates a glove configuration.
Request:
{
"defindex": 5027,
"active": true,
"paintIndex": 10006,
"wear": 0.15,
"pattern": 661,
"team": 1,
"reset": false
}
Response:
{
"success": true,
"message": "Gloves updated successfully"
}
Fetches weapons of a specific type (rifles, pistols, smgs, heavys) for a loadout.
Response:
{
"weapons": [
{
"weapon_defindex": 7,
"defaultName": "AK-47",
"paintIndex": 490,
"defaultImage": "https://example.com/ak47.png",
"weapon_name": "AK-47",
"category": "rifle",
"availableTeams": "t",
"name": "AK-47 | Asiimov",
"image": "https://example.com/ak47_asiimov.png",
"minFloat": 0.05,
"maxFloat": 0.7,
"rarity": {
"name": "Covert",
"id": "covert",
"color": "#eb4b4b"
},
"databaseInfo": {
"active": true,
"team": 1,
"defindex": 7,
"paintIndex": 490,
"pattern": 661,
"wear": 0.05,
"statTrak": true,
"statTrakCount": 1337,
"nameTag": "My AK",
"stickers": [
{
"id": 4,
"x": 0,
"y": 0,
"wear": 0,
"scale": 1,
"rotation": 0,
"api": {
"name": "Sticker | Crown (Foil)",
"type": "sticker",
"image": "https://example.com/crown_foil.png",
"effect": "foil",
"rarity": {
"name": "Extraordinary",
"id": "extraordinary",
"color": "#eb4b4b"
},
"tournament_team": "",
"tournament_event": ""
}
}
],
"keychain": {
"id": 4187,
"x": 0,
"y": 0,
"z": 0,
"seed": 0,
"api": {
"name": "Chicken Capsule",
"image": "https://example.com/chicken_capsule.png",
"rarity": {
"name": "Extraordinary",
"id": "extraordinary",
"color": "#eb4b4b"
}
}
}
}
}
]
}
Saves or updates a weapon configuration.
Request:
{
"defindex": 7,
"active": true,
"paintIndex": 490,
"wear": 0.05,
"pattern": 661,
"statTrak": true,
"statTrakCount": 1337,
"nameTag": "My AK",
"team": 1,
"stickers": [
{
"slot": 0,
"stickerId": 4,
"wear": 0,
"scale": 1,
"rotation": 0
}
],
"keychain": {
"id": 4187,
"seed": 0
},
"reset": false
}
Response:
{
"success": true,
"message": "Weapon updated successfully"
}
The application requires several environment variables to function properly. Create a .env
file in the root directory:
# Server Configuration
PORT=3000
HOST=127.0.0.1
# JWT Configuration (Required)
JWT_TOKEN=your-super-secret-jwt-key-here
JWT_EXPIRY=7d
# Database Configuration (Required)
DATABASE_HOST=localhost
DATABASE_PORT=3306
DATABASE_USER=your-db-user
DATABASE_PASSWORD=your-db-password
DATABASE_NAME=cs2inspect
DATABASE_CONNECTION_LIMIT=5
# Steam API (Optional - for enhanced features)
STEAM_API_KEY=your-steam-api-key
Variable | Description | Required | Default |
---|---|---|---|
PORT |
Server port | No | 3000 |
HOST |
Server host | No | 127.0.0.1 |
JWT_TOKEN |
Secret key for JWT tokens | Yes | - |
JWT_EXPIRY |
JWT token expiration time | No | 7d |
DATABASE_HOST |
Database server host | Yes | - |
DATABASE_PORT |
Database server port | No | 3306 |
DATABASE_USER |
Database username | Yes | - |
DATABASE_PASSWORD |
Database password | Yes | - |
DATABASE_NAME |
Database name | Yes | - |
DATABASE_CONNECTION_LIMIT |
Max database connections | No | 5 |
STEAM_API_KEY |
Steam Web API key | No | - |
The application uses Steam's OpenID 2.0 authentication system:
- Login Flow: Users click "Login with Steam" β Redirected to Steam β Steam validates β Redirected back with identity
- JWT Tokens: Upon successful authentication, a JWT token is issued and stored as an HTTP-only cookie
- Session Management: JWT tokens are validated on each API request to protected endpoints
- Automatic Logout: Invalid or expired tokens trigger automatic logout
- HTTP-Only Cookies: JWT tokens stored securely to prevent XSS attacks
- CORS Protection: Configured for specific origins in production
- Input Validation: All API inputs are validated and sanitized
- SQL Injection Prevention: Parameterized queries used throughout
- Rate Limiting: Built-in protection against abuse (via Nitro)
- CS2 Item Data: Cached locally in
/storage/csgo-api/
to reduce external API calls - Automatic Updates: Data refreshed periodically to stay current with game updates
- Memory Caching: Frequently accessed data cached in memory using
node-cache
- Server-Side Rendering: Initial page loads are server-rendered for better SEO and performance
- Code Splitting: Automatic code splitting by Nuxt for optimal bundle sizes
- Image Optimization: Lazy loading and optimized image delivery
- Prefetching: Critical resources prefetched for smooth navigation
- Node.js (v18+ recommended)
- MariaDB/MySQL (v10.3+ / v8.0+)
- Bun (recommended) or npm/yarn/pnpm
# Clone the repository
git clone https://github.com/your-username/cs2inspect-web.git
cd cs2inspect-web
# Install dependencies (using Bun - recommended)
bun install
# Or using other package managers
npm install
# yarn install
# pnpm install
-
Create Database:
CREATE DATABASE cs2inspect CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-
Import Schema:
mysql -u your-username -p cs2inspect < server/database/init.sql
-
Configure Environment: Copy
.env.example
to.env
and update database credentials
# Start development server
bun run dev
# or npm run dev
# Server will start on http://localhost:3000
# Build for production
bun run build
# Preview production build
bun run preview
# Start production server
node .output/server/index.mjs
# Run unit tests
bun run test
# or npm run test
# Run tests in watch mode
bun run test:watch
# Generate coverage report
bun run test:coverage
# Run E2E tests
bun run test:e2e
# Run E2E tests in headed mode
bun run test:e2e:headed
# Run ESLint
bun run lint
# Fix linting issues
bun run lint:fix
-
Build the Application:
bun run build
-
Database Migration: Ensure your production database has the latest schema from
server/database/init.sql
-
Environment Variables: Set all required environment variables in your production environment
-
Start the Server:
node .output/server/index.mjs
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["node", ".output/server/index.mjs"]
For production, use a reverse proxy like Nginx:
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
We welcome contributions to CS2 Loadout Manager! Here's how you can help:
- Fork the Repository
- Create a Feature Branch:
git checkout -b feature/your-feature-name
- Make Your Changes
- Run Tests:
bun run test bun run lint
- Commit Your Changes:
git commit -m "feat: add your feature description"
- Push to Your Fork:
git push origin feature/your-feature-name
- Create a Pull Request
- TypeScript: Use TypeScript for all new code
- ESLint: Follow the project's ESLint configuration
- Conventional Commits: Use conventional commit messages
- Component Structure: Follow Vue 3 Composition API patterns
- Testing: Write tests for new features and bug fixes
- π Bug Fixes: Report and fix bugs
- β¨ New Features: Implement new functionality
- π Translations: Add support for new languages
- π Documentation: Improve documentation and examples
- π¨ UI/UX: Enhance user interface and experience
- β‘ Performance: Optimize application performance
This project is licensed under the MIT License - see the LICENSE file for details.
- Valve Corporation - For Counter-Strike 2 and Steam APIs
- CSGO-API Contributors - For maintaining the comprehensive CS2 item database
- Nuxt Team - For the excellent full-stack framework
- Vue.js Team - For the progressive JavaScript framework
- Open Source Community - For all the amazing libraries and tools
If you encounter any issues or have questions:
- Check the Documentation - Most common issues are covered here
- Search Issues - Look through existing GitHub issues
- Create an Issue - If you can't find a solution, create a new issue
- Join Discussions - Participate in GitHub Discussions for general questions
Made with β€οΈ for the Counter-Strike 2 community