diff --git a/Dockerfile b/Dockerfile index 807eb44..703d96b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,14 +11,25 @@ RUN apt-get update -qq && \ python-is-python3 \ curl \ ca-certificates \ - unzip && \ - rm -rf /var/lib/apt/lists/* + unzip \ + libc6 \ + && rm -rf /var/lib/apt/lists/* FROM base-node AS build-main # Copy package files COPY website/package.json website/package-lock.json* ./ -RUN npm install --include=dev -COPY website/. ./ + +# Install dependencies with platform-specific binaries +RUN npm install --include=dev --platform=linux --arch=x64 + +# Copy the rest of the application +COPY website/. . + +# Create .svelte-kit directory if it doesn't exist +RUN mkdir -p .svelte-kit + +# Generate SvelteKit types and build + RUN npm run build FROM base-node AS build-websocket diff --git a/README.md b/README.md index a001296..b9a3126 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

Rugplay.com - the fake crypto simulator.

-[Privacy Policy](https://rugplay.com/legal/privacy) | [Terms of Service](https://rugplay.com/legal/terms) | [License](LICENSE) | [YouTube video](https://rugplay.com) +[Privacy Policy](https://rugplay.com/legal/privacy) | [Terms of Service](https://rugplay.com/legal/terms) | [License](LICENSE) | [YouTube video](https://www.youtube.com/watch?v=nRUkvPMphRc) ## About @@ -18,34 +18,175 @@ Rugplay is a realistic cryptocurrency trading simulator that lets you experience ![Preview 2](github_assets/preview2.png) ![Preview](github_assets/preview.png) -## Setup +## Development Setup -1. Copy the environment file: +### Prerequisites - ```bash - cp website/.env.example website/.env - ``` +Before you begin, make sure you have the following installed: -2. Edit `website/.env` with your actual values. +- [Node.js](https://nodejs.org/) (LTS version recommended) +- [Bun](https://bun.sh/) (for websocket server) +- [Docker](https://www.docker.com/) (for running Redis and PostgreSQL) -3. Start with Docker (recommended): +### Getting Started + +1. **Clone the repository** + + ```bash + git clone https://github.com/outpoot/rugplay.git + cd rugplay + ``` + +2. **Set up Redis** + + Create a Redis Docker container: + ```bash + docker run -d --name rugplay-redis -p 6379:6379 -v rugplay_redisdata:/data --restart unless-stopped redis:8-alpine redis-server --save 60 1 + ``` + + **Alternative:** You can also [download and install Redis directly](https://redis.io/downloads/) for your operating system, or use a managed Redis service such as Redis Cloud. + +3. **Set up PostgreSQL** + + You have two main options for your Postgres database: + + - **Run Postgres locally with Docker:** + ```bash + docker run -d --name rugplay-postgres -e POSTGRES_USER=pguser -e POSTGRES_PASSWORD=pgpass -e POSTGRES_DB=rugplay -p 5432:5432 -v rugplay_pgdata:/var/lib/postgresql/data --restart unless-stopped pgvector/pgvector:pg16 + ``` + + - **Use a managed cloud Postgres provider:** + Services like [Supabase](https://supabase.com/), [Neon](https://neon.tech/), or others let you create a Postgres database online, often with a free tier. Simply grab your connection details (host, user, password, database name) from your provider. + + After your database is running, update your `DATABASE_URL` in the `.env` file to point to your Postgres instance. + + If you are running Postgres locally, you can seed the database with: + ```bash + docker exec -it rugplay-postgres psql -d rugplay -U pguser + ``` + Then, copy and paste the SQL from the migration files (e.g. `website/drizzle/0000_crazy_bloodstrike.sql`). + +4. **Configure Google OAuth** + + - Go to [Google Auth Platform dashboard](https://console.cloud.google.com/auth/clients) + - Create a new client: + - Application type: "Web application" + - Name: Your choice + - Authorized JavaScript origins: Can be left empty + - Authorized redirect URIs: + - http://localhost:3002/api/auth/callback/google + - http://localhost:5173/api/auth/callback/google + - Production URL + +5. **Configure Environment Variables** + + ```bash + cd website + cp .env.example .env + ``` + + Edit `.env` with your values. Example: + + ```ini + # --- Database --- + DATABASE_URL=postgres://pguser:pgpass@localhost:5432/rugplay # PostgreSQL connection string + POSTGRES_USER=pguser # PostgreSQL username (should match Docker config) + POSTGRES_PASSWORD=pgpass # PostgreSQL password (should match Docker config) + POSTGRES_DB=rugplay # PostgreSQL database name (should match Docker config) + + # --- Redis --- + REDIS_URL=redis://localhost:6379 # Redis connection string + + # --- Auth --- + PRIVATE_BETTER_AUTH_SECRET=your_secret_here # Any alphanumeric string for session encryption + PUBLIC_BETTER_AUTH_URL=http://localhost:5173 # For development, use http://localhost:5173. For production, use http://localhost:3002 + + # --- Google OAuth --- + GOOGLE_CLIENT_ID=your_google_client_id + GOOGLE_CLIENT_SECRET=your_google_client_secret + + # --- Websocket --- + PUBLIC_WEBSOCKET_URL=ws://localhost:8080 # URL of the websocket server + + # --- Optional: AWS (for additional features) --- + AWS_ACCESS_KEY_ID=your_aws_access_key + AWS_SECRET_ACCESS_KEY=your_aws_secret_key + AWS_REGION=us-east-1 + + # --- Optional: OpenRouter (for AI features) --- + OPENROUTER_API_KEY=your_openrouter_api_key + ``` + + **Notes:** + - For production, change `PUBLIC_BETTER_AUTH_URL` to `http://localhost:3002` or your deployed domain. + - The `DATABASE_URL` should match your Postgres Docker container settings. + - The `REDIS_URL` should match your Redis Docker container settings. + - AWS and OpenRouter variables are optional and only needed for advanced features. + +6. **Install Dependencies** + + Set up the websocket server: + ```bash + cd website/websocket + bun install + ``` + + Set up the main website: + ```bash + cd ../ + npm install --include=dev + ``` + +### Running the Application + +1. **Start the websocket server** + + ```bash + cd website/websocket + bun run src/main.ts + ``` + +2. **Start the main website (Development Mode)** + + In a new terminal: + ```bash + cd website + npm run dev + ``` + + The development server will be available at http://localhost:5173 + +### Production Deployment + +#### Using Docker (Recommended) + +1. **Build and start with Docker:** ```bash ./build.sh ``` -4. The app will be available at http://localhost:3002 + This will automatically start all required services, including the Database, Redis and the websocket server, as Docker containers. You do not need to run the websocket server manually when using Docker Compose for deployment. -## Development +2. The app will be available at http://localhost:3002 -```bash -cd website -npm install -npm run dev -``` +#### Manual Deployment -The development server runs on http://localhost:5173 +1. **Build the website:** + + ```bash + cd website + npm run build + npm run preview + ``` + +2. **Start the websocket server:** + + ```bash + cd website/websocket + bun run src/main.ts + ``` ## License -This project is licensed under the **Creative Commons Attribution-NonCommercial 4.0 International** License (**CC BY-NC 4.0**). See the [LICENSE](LICENSE) file for details. \ No newline at end of file +This project is licensed under the **Creative Commons Attribution-NonCommercial 4.0 International** License (**CC BY-NC 4.0**). See the [LICENSE](LICENSE) file for details. diff --git a/build.sh b/build.sh index 14b984a..ba0e1e3 100644 --- a/build.sh +++ b/build.sh @@ -1,10 +1,6 @@ #!/bin/bash -set -e - -cd "$(dirname "$0")" echo "🚀 Starting Rugplay deployment..." - echo "📥 Pulling latest changes..." git pull diff --git a/docker-compose.yml b/docker-compose.yml index e41561b..d577469 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3.8' - services: app: build: diff --git a/website/.env.example b/website/.env.example index 50db989..a25398c 100644 --- a/website/.env.example +++ b/website/.env.example @@ -12,7 +12,7 @@ REDIS_URL=redis://redis:6379 # Authentication PRIVATE_BETTER_AUTH_SECRET=your_super_secret_auth_key_here -PUBLIC_BETTER_AUTH_URL=http://localhost:3002 +PUBLIC_BETTER_AUTH_URL=http://localhost:5173 # Change for Production to http://localhost:3002 # Google OAuth (optional - for social login) GOOGLE_CLIENT_ID=your_google_client_id @@ -26,4 +26,7 @@ PUBLIC_B2_ENDPOINT=https://s3.us-west-002.backblazeb2.com PUBLIC_B2_REGION=us-west-002 # OpenAI (for AI features) -OPENROUTER_API_KEY=your_openrouter_api_key \ No newline at end of file +OPENROUTER_API_KEY=your_openrouter_api_key + +# Websocket URL +PUBLIC_WEBSOCKET_URL=http://localhost:8080 \ No newline at end of file diff --git a/website/README.md b/website/README.md index b5b2950..19d5f59 100644 --- a/website/README.md +++ b/website/README.md @@ -1,38 +1,65 @@ -# sv +# Rugplay Website -Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli). +This is the main website component of Rugplay, built with SvelteKit. It handles the user interface, trading functionality, and market visualization. -## Creating a project +## Development -If you're seeing this, you've probably already done this step. Congrats! +### Prerequisites + +- Node.js (LTS version) +- Redis running in the background +- OpenRouter API key (for AI features) +- AWS S3/B2 Storage (for file uploads) + +### Environment Variables + +Create a `.env` file based on `.env.example`: ```bash -# create a new project in the current directory -npx sv create - -# create a new project in my-app -npx sv create my-app +cp .env.example .env ``` -## Developing +Key variables to configure: +- `PUBLIC_BETTER_AUTH_URL`: Set to `http://localhost:3002` if you want to deploy +- `OPENROUTER_API_KEY`: Your OpenRouter API key for AI features +- AWS credentials (optional but recommended) -Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: +### Running in Development ```bash +npm install npm run dev - -# or start the server and open the app in a new browser tab -npm run dev -- --open ``` -## Building +The development server will be available at http://localhost:5173 -To create a production version of your app: +### Building for Production ```bash npm run build +npm run preview ``` -You can preview the production build with `npm run preview`. +## Project Structure -> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment. +- `src/routes/`: Page components and API endpoints +- `src/lib/`: Shared components and utilities +- `src/lib/components/`: Reusable UI components +- `static/`: Static assets (images, fonts, etc.) + +## Features + +- User authentication and profile management +- Real-time trading interface +- Market visualization with Treemap +- Leaderboards and statistics +- Integration with websocket server for live updates + +## Contributing + +1. Make sure Redis is running +2. Start the websocket server (see `websocket/README.md`) +3. Run the website in development mode +4. Make your changes +5. Test thoroughly +6. Submit a pull request diff --git a/website/package-lock.json b/website/package-lock.json index f0405e0..c2675db 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -10,7 +10,6 @@ "dependencies": { "@aws-sdk/client-s3": "^3.815.0", "@aws-sdk/s3-request-presigner": "^3.815.0", - "@sveltejs/adapter-node": "^5.2.12", "@tailwindcss/postcss": "^4.1.7", "@tailwindcss/typography": "^0.5.16", "@visx/scale": "^3.12.0", @@ -34,6 +33,7 @@ "@internationalized/date": "^3.8.1", "@lucide/svelte": "^0.482.0", "@sveltejs/adapter-auto": "^3.0.0", + "@sveltejs/adapter-node": "^5.2.12", "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^4.0.0", "@types/canvas-confetti": "^1.9.0", @@ -57,7 +57,8 @@ "vite-plugin-iso-import": "^1.2.0" }, "optionalDependencies": { - "@rollup/rollup-linux-x64-gnu": "*" + "@rollup/rollup-linux-x64-gnu": "*", + "lightningcss-linux-x64-gnu": "*" } }, "node_modules/@alloc/quick-lru": { @@ -1507,6 +1508,7 @@ }, "node_modules/@polka/url": { "version": "1.0.0-next.29", + "dev": true, "license": "MIT" }, "node_modules/@redis/bloom": { @@ -1563,6 +1565,7 @@ "version": "28.0.3", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.3.tgz", "integrity": "sha512-pyltgilam1QPdn+Zd9gaCfOLcnjMEJ9gV+bTw6/r73INdvzf1ah9zLIJBm+kW7R6IUFIQ1YO+VqZtYxZNWFPEQ==", + "dev": true, "dependencies": { "@rollup/pluginutils": "^5.0.1", "commondir": "^1.0.1", @@ -1588,6 +1591,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, "dependencies": { "@types/estree": "*" } @@ -1596,6 +1600,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==", + "dev": true, "dependencies": { "@rollup/pluginutils": "^5.1.0" }, @@ -1615,6 +1620,7 @@ "version": "16.0.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.1.tgz", "integrity": "sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==", + "dev": true, "dependencies": { "@rollup/pluginutils": "^5.0.1", "@types/resolve": "1.20.2", @@ -1638,6 +1644,7 @@ "version": "5.1.4", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", + "dev": true, "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", @@ -1662,6 +1669,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "android" @@ -1674,6 +1682,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "android" @@ -1686,6 +1695,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -1698,6 +1708,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -1710,6 +1721,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "freebsd" @@ -1722,6 +1734,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "freebsd" @@ -1734,6 +1747,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1746,6 +1760,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1758,6 +1773,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1770,6 +1786,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1782,6 +1799,7 @@ "cpu": [ "loong64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1794,6 +1812,7 @@ "cpu": [ "ppc64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1806,6 +1825,7 @@ "cpu": [ "riscv64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1818,6 +1838,7 @@ "cpu": [ "riscv64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1830,6 +1851,7 @@ "cpu": [ "s390x" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1854,6 +1876,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1866,6 +1889,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -1878,6 +1902,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "win32" @@ -1888,6 +1913,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2554,6 +2580,8 @@ "version": "5.2.12", "resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.2.12.tgz", "integrity": "sha512-0bp4Yb3jKIEcZWVcJC/L1xXp9zzJS4hDwfb4VITAkfT4OVdkspSHsx7YhqJDbb2hgLl6R9Vs7VQR+fqIVOxPUQ==", + "dev": true, + "license": "MIT", "dependencies": { "@rollup/plugin-commonjs": "^28.0.1", "@rollup/plugin-json": "^6.1.0", @@ -2566,6 +2594,7 @@ }, "node_modules/@sveltejs/kit": { "version": "2.21.1", + "dev": true, "license": "MIT", "dependencies": { "@sveltejs/acorn-typescript": "^1.0.5", @@ -2595,6 +2624,7 @@ }, "node_modules/@sveltejs/vite-plugin-svelte": { "version": "4.0.4", + "dev": true, "license": "MIT", "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^3.0.0-next.0||^3.0.0", @@ -2614,6 +2644,7 @@ }, "node_modules/@sveltejs/vite-plugin-svelte-inspector": { "version": "3.0.1", + "dev": true, "license": "MIT", "dependencies": { "debug": "^4.3.7" @@ -2777,6 +2808,7 @@ }, "node_modules/@types/cookie": { "version": "0.6.0", + "dev": true, "license": "MIT" }, "node_modules/@types/d3-array": { @@ -2850,7 +2882,8 @@ "node_modules/@types/resolve": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", - "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==" + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true }, "node_modules/@visx/scale": { "version": "3.12.0", @@ -3315,7 +3348,8 @@ "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true }, "node_modules/content-disposition": { "version": "1.0.0", @@ -3338,6 +3372,7 @@ }, "node_modules/cookie": { "version": "0.6.0", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -3461,6 +3496,7 @@ }, "node_modules/deepmerge": { "version": "4.3.1", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -3501,6 +3537,7 @@ }, "node_modules/devalue": { "version": "5.1.1", + "dev": true, "license": "MIT" }, "node_modules/drizzle-kit": { @@ -3794,7 +3831,8 @@ "node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true }, "node_modules/etag": { "version": "1.8.1", @@ -3905,6 +3943,7 @@ }, "node_modules/fdir": { "version": "6.4.4", + "dev": true, "license": "MIT", "peerDependencies": { "picomatch": "^3 || ^4" @@ -3991,6 +4030,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -4181,6 +4221,7 @@ "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, "dependencies": { "hasown": "^2.0.2" }, @@ -4194,7 +4235,8 @@ "node_modules/is-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==" + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true }, "node_modules/is-promise": { "version": "4.0.0", @@ -4224,6 +4266,7 @@ }, "node_modules/kleur": { "version": "4.1.5", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4262,6 +4305,26 @@ "lightningcss-win32-x64-msvc": "1.30.1" } }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/lightningcss-win32-x64-msvc": { "version": "1.30.1", "cpu": [ @@ -4447,6 +4510,7 @@ }, "node_modules/mri": { "version": "1.2.0", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -4454,6 +4518,7 @@ }, "node_modules/mrmime": { "version": "2.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -4628,7 +4693,8 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "node_modules/path-to-regexp": { "version": "8.2.0", @@ -4646,6 +4712,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, "engines": { "node": ">=12" }, @@ -4887,6 +4954,7 @@ "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", @@ -4916,6 +4984,7 @@ }, "node_modules/rollup": { "version": "4.41.0", + "dev": true, "license": "MIT", "dependencies": { "@types/estree": "1.0.7" @@ -4958,6 +5027,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -4999,6 +5069,7 @@ }, "node_modules/sade": { "version": "1.8.1", + "dev": true, "license": "MIT", "dependencies": { "mri": "^1.1.0" @@ -5223,6 +5294,7 @@ }, "node_modules/sirv": { "version": "3.0.1", + "dev": true, "license": "MIT", "dependencies": { "@polka/url": "^1.0.0-next.24", @@ -5286,6 +5358,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -5572,6 +5645,7 @@ }, "node_modules/totalist": { "version": "3.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -5706,6 +5780,7 @@ }, "node_modules/vite": { "version": "5.4.19", + "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.21.3", @@ -5776,6 +5851,7 @@ }, "node_modules/vite/node_modules/esbuild": { "version": "0.21.5", + "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -5815,6 +5891,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5826,6 +5903,7 @@ }, "node_modules/vitefu": { "version": "1.0.6", + "dev": true, "license": "MIT", "workspaces": [ "tests/deps/*", diff --git a/website/package.json b/website/package.json index de2c4ba..cccea7a 100644 --- a/website/package.json +++ b/website/package.json @@ -19,6 +19,7 @@ "@internationalized/date": "^3.8.1", "@lucide/svelte": "^0.482.0", "@sveltejs/adapter-auto": "^3.0.0", + "@sveltejs/adapter-node": "^5.2.12", "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^4.0.0", "@types/canvas-confetti": "^1.9.0", @@ -44,7 +45,6 @@ "dependencies": { "@aws-sdk/client-s3": "^3.815.0", "@aws-sdk/s3-request-presigner": "^3.815.0", - "@sveltejs/adapter-node": "^5.2.12", "@tailwindcss/postcss": "^4.1.7", "@tailwindcss/typography": "^0.5.16", "@visx/scale": "^3.12.0", @@ -65,6 +65,7 @@ "svelte-lightweight-charts": "^2.2.0" }, "optionalDependencies": { - "@rollup/rollup-linux-x64-gnu": "*" + "@rollup/rollup-linux-x64-gnu": "*", + "lightningcss-linux-x64-gnu": "*" } } diff --git a/website/src/lib/auth.ts b/website/src/lib/auth.ts index a29f0bb..b53adf9 100644 --- a/website/src/lib/auth.ts +++ b/website/src/lib/auth.ts @@ -1,23 +1,27 @@ // src/lib/auth.ts (or your auth config file) import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; -import { env } from '$env/dynamic/private'; +import { env as privateEnv } from '$env/dynamic/private'; +import { env as publicEnv } from '$env/dynamic/public'; import { db } from "./server/db"; import * as schema from "./server/db/schema"; import { generateUsername } from "./utils/random"; import { uploadProfilePicture } from "./server/s3"; import { apiKey } from "better-auth/plugins"; -if (!env.GOOGLE_CLIENT_ID) throw new Error('GOOGLE_CLIENT_ID is not set'); -if (!env.GOOGLE_CLIENT_SECRET) throw new Error('GOOGLE_CLIENT_SECRET is not set'); +if (!privateEnv.GOOGLE_CLIENT_ID) throw new Error('GOOGLE_CLIENT_ID is not set'); +if (!privateEnv.GOOGLE_CLIENT_SECRET) throw new Error('GOOGLE_CLIENT_SECRET is not set'); +if (!publicEnv.PUBLIC_BETTER_AUTH_URL) throw new Error('PUBLIC_BETTER_AUTH_URL is not set'); export const auth = betterAuth({ - baseURL: env.PUBLIC_BETTER_AUTH_URL, - secret: env.PRIVATE_BETTER_AUTH_SECRET, + baseURL: publicEnv.PUBLIC_BETTER_AUTH_URL, + secret: privateEnv.PRIVATE_BETTER_AUTH_SECRET, appName: "Rugplay", trustedOrigins: [ - env.BETTER_AUTH_URL, "http://rugplay.com", "http://localhost:5173", + publicEnv.PUBLIC_BETTER_AUTH_URL, + "http://rugplay.com", + "http://localhost:5173", ], plugins: [ @@ -41,8 +45,8 @@ export const auth = betterAuth({ }), socialProviders: { google: { - clientId: env.GOOGLE_CLIENT_ID, - clientSecret: env.GOOGLE_CLIENT_SECRET, + clientId: privateEnv.GOOGLE_CLIENT_ID, + clientSecret: privateEnv.GOOGLE_CLIENT_SECRET, mapProfileToUser: async (profile) => { const newUsername = generateUsername(); let s3ImageKey: string | null = null; @@ -78,7 +82,7 @@ export const auth = betterAuth({ user: { additionalFields: { username: { type: "string", required: true, input: false }, - isAdmin: { type: "boolean", required: false, input: false }, + isAdmin: { type: "boolean", required: true, input: false }, isBanned: { type: "boolean", required: false, input: false }, banReason: { type: "string", required: false, input: false }, baseCurrencyBalance: { type: "string", required: false, input: false }, diff --git a/website/src/lib/components/self/UserManualModal.svelte b/website/src/lib/components/self/UserManualModal.svelte index 275966f..7720566 100644 --- a/website/src/lib/components/self/UserManualModal.svelte +++ b/website/src/lib/components/self/UserManualModal.svelte @@ -203,13 +203,15 @@
- {#each tips as _, index} + {#each tips as tip, index} {/each}
diff --git a/website/src/lib/components/self/games/Mines.svelte b/website/src/lib/components/self/games/Mines.svelte index 17b0aaa..a12b44e 100644 --- a/website/src/lib/components/self/games/Mines.svelte +++ b/website/src/lib/components/self/games/Mines.svelte @@ -105,7 +105,14 @@ async function handleTileClick(index: number) { if (!isPlaying || revealedTiles.includes(index) || !sessionToken) return; lastClickedTile = index; - try { + + // Temporarily disabled - using local simulation + toast.error('Mines game temporarily disabled', { + description: 'This feature is currently under maintenance' + }); + return; + + /* try { const response = await fetch('/api/gambling/mines/reveal', { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -142,12 +149,19 @@ toast.error('Failed to reveal tile', { description: error instanceof Error ? error.message : 'Unknown error occurred' }); - } + } */ } async function cashOut() { if (!isPlaying || !sessionToken) return; - try { + + // Temporarily disabled + toast.error('Mines game temporarily disabled', { + description: 'This feature is currently under maintenance' + }); + return; + + /* try { const response = await fetch('/api/gambling/mines/cashout', { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -175,12 +189,19 @@ toast.error('Failed to cash out', { description: error instanceof Error ? error.message : 'Unknown error occurred' }); - } + } */ } async function startGame() { if (!canBet) return; - balance -= betAmount; + + // Temporarily disabled + toast.error('Mines game temporarily disabled', { + description: 'This feature is currently under maintenance' + }); + return; + + /* balance -= betAmount; onBalanceUpdate?.(balance); try { const response = await fetch('/api/gambling/mines/start', { @@ -207,7 +228,7 @@ toast.error('Failed to start game', { description: error instanceof Error ? error.message : 'Unknown error occurred' }); - } + } */ } onMount(async () => { diff --git a/website/src/lib/utils.ts b/website/src/lib/utils.ts index cb8030a..f5b1e30 100644 --- a/website/src/lib/utils.ts +++ b/website/src/lib/utils.ts @@ -63,6 +63,8 @@ export function formatPrice(price: number): string { export function formatValue(value: number | string): string { const numValue = typeof value === 'string' ? parseFloat(value) : value; if (typeof numValue !== 'number' || isNaN(numValue)) return '$0.00'; + + if (numValue >= 1e12) return `$${(numValue / 1e12).toFixed(2)}T`; if (numValue >= 1e9) return `$${(numValue / 1e9).toFixed(2)}B`; if (numValue >= 1e6) return `$${(numValue / 1e6).toFixed(2)}M`; if (numValue >= 1e3) return `$${(numValue / 1e3).toFixed(2)}K`; diff --git a/website/src/routes/api/gambling/dice/+server.ts b/website/src/routes/api/gambling/dice/+server.ts index d023cf6..fd6db2f 100644 --- a/website/src/routes/api/gambling/dice/+server.ts +++ b/website/src/routes/api/gambling/dice/+server.ts @@ -3,7 +3,7 @@ import { error, json } from '@sveltejs/kit'; import { db } from '$lib/server/db'; import { user } from '$lib/server/db/schema'; import { eq } from 'drizzle-orm'; -import { randomBytes } from 'crypto'; +import { randomInt } from 'crypto'; import type { RequestHandler } from './$types'; interface DiceRequest { @@ -54,7 +54,7 @@ export const POST: RequestHandler = async ({ request }) => { throw new Error(`Insufficient funds. You need *${roundedAmount.toFixed(2)} but only have *${roundedBalance.toFixed(2)}`); } - const gameResult = Math.floor(randomBytes(1)[0] / 42.67) + 1; // This gives us a number between 1-6 + const gameResult = randomInt(1, 6); const won = gameResult === selectedNumber; const multiplier = 3; diff --git a/website/src/routes/api/gambling/mines/cashout/+server.ts b/website/src/routes/api/gambling/mines/cashout/+server.ts index dd9b7a7..b417fae 100644 --- a/website/src/routes/api/gambling/mines/cashout/+server.ts +++ b/website/src/routes/api/gambling/mines/cashout/+server.ts @@ -8,6 +8,8 @@ import { getSessionKey } from '$lib/server/games/mines'; import type { RequestHandler } from './$types'; export const POST: RequestHandler = async ({ request }) => { + throw error(503, 'Service temporarily unavailable'); + const session = await auth.api.getSession({ headers: request.headers }); diff --git a/website/src/routes/api/gambling/mines/reveal/+server.ts b/website/src/routes/api/gambling/mines/reveal/+server.ts index e36744d..eff6ced 100644 --- a/website/src/routes/api/gambling/mines/reveal/+server.ts +++ b/website/src/routes/api/gambling/mines/reveal/+server.ts @@ -9,6 +9,8 @@ import { redis, } from '$lib/server/redis'; import { getSessionKey } from '$lib/server/games/mines'; export const POST: RequestHandler = async ({ request }) => { + throw error(503, 'Service temporarily unavailable'); + const session = await auth.api.getSession({ headers: request.headers }); @@ -40,7 +42,7 @@ export const POST: RequestHandler = async ({ request }) => { if (game.minePositions.includes(tileIndex)) { game.status = 'lost'; const minePositions = game.minePositions; - + const userId = Number(session.user.id); const [userData] = await db .select({ baseCurrencyBalance: user.baseCurrencyBalance }) @@ -58,9 +60,9 @@ export const POST: RequestHandler = async ({ request }) => { updatedAt: new Date() }) .where(eq(user.id, userId)); - + await redis.del(getSessionKey(sessionToken)); - + return json({ hitMine: true, minePositions, diff --git a/website/src/routes/api/gambling/mines/start/+server.ts b/website/src/routes/api/gambling/mines/start/+server.ts index b7ab5f6..e7ad4b4 100644 --- a/website/src/routes/api/gambling/mines/start/+server.ts +++ b/website/src/routes/api/gambling/mines/start/+server.ts @@ -8,6 +8,8 @@ import { getSessionKey } from '$lib/server/games/mines'; import type { RequestHandler } from './$types'; export const POST: RequestHandler = async ({ request }) => { + throw error(503, 'Service temporarily unavailable'); + const session = await auth.api.getSession({ headers: request.headers }); @@ -51,7 +53,7 @@ export const POST: RequestHandler = async ({ request }) => { } // transaction token for authentication stuff - const randomBytes = new Uint8Array(8); + const randomBytes = new Uint8Array(8); crypto.getRandomValues(randomBytes); const sessionToken = Array.from(randomBytes) .map(b => b.toString(16).padStart(2, '0')) @@ -85,7 +87,7 @@ export const POST: RequestHandler = async ({ request }) => { }) .where(eq(user.id, userId)); - return { + return { sessionToken, newBalance }; diff --git a/website/websocket/README.md b/website/websocket/README.md index 7d7d5dc..6151e70 100644 --- a/website/websocket/README.md +++ b/website/websocket/README.md @@ -1,15 +1,32 @@ -# websocket +# Websocket Server -To install dependencies: +This is the websocket server component of Rugplay, built with [Bun](https://bun.sh) - a fast all-in-one JavaScript runtime. + +## Prerequisites + +- [Bun](https://bun.sh) (v1.2.11 or later) +- [Redis](https://redis.io/downloads/) running in the background + +## Development + +### Initial Setup ```bash bun install ``` -To run: +### Running the Server ```bash bun run src/main.ts ``` -This project was created using `bun init` in bun v1.2.11. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. +## Production + +For production deployment, you do not need to start the WebSocket server separately. Simply run `build.sh` in the main directory, and the WebSocket server will be included as part of the deployment process. + +## Notes + +- This project was created using `bun init` in bun v1.2.11 +- The websocket server handles real-time updates for the trading platform +- Make sure Redis is running before starting the server