init
This commit is contained in:
commit
6c644fd434
42
.gitignore
vendored
Normal file
42
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Dependencies
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Build output
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# IDE files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
logs/
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids/
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
159
README.md
Normal file
159
README.md
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
# DMRocket API
|
||||
|
||||
A Node.js/Express API for managing orders with MySQL database integration.
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Install Dependencies
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
### 2. Database Setup
|
||||
Create your MySQL database and table:
|
||||
|
||||
```sql
|
||||
CREATE DATABASE dmrocket;
|
||||
USE dmrocket;
|
||||
|
||||
CREATE TABLE `Orders` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`wallet` TEXT NOT NULL,
|
||||
`mint` TEXT NOT NULL,
|
||||
`amount` BIGINT NOT NULL DEFAULT '100',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`user_id` TEXT NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE = InnoDB;
|
||||
```
|
||||
|
||||
### 3. Environment Configuration
|
||||
Create a `.env.local` file in the root directory with your database credentials:
|
||||
|
||||
```env
|
||||
# Database Configuration
|
||||
DB_HOST=localhost
|
||||
DB_USER=root
|
||||
DB_PASSWORD=your_password_here
|
||||
DB_NAME=dmrocket
|
||||
DB_PORT=3306
|
||||
|
||||
# Server Configuration
|
||||
PORT=7111
|
||||
NODE_ENV=development
|
||||
```
|
||||
|
||||
### 4. Run the Application
|
||||
```bash
|
||||
# Development mode
|
||||
npm run dev
|
||||
|
||||
# Production build
|
||||
npm run build
|
||||
npm start
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
All endpoints use GET requests for simplicity and ease of use.
|
||||
|
||||
### Orders API
|
||||
|
||||
#### Create Order
|
||||
- **GET** `/api/v1/create_order`
|
||||
- **Query Parameters:**
|
||||
- `wallet` (required) - Wallet address
|
||||
- `mint` (required) - Mint address
|
||||
- `amount` (optional) - Order amount (default: 100)
|
||||
- `user_id` (required) - User identifier
|
||||
|
||||
**Example:**
|
||||
```
|
||||
GET /api/v1/create_order?wallet=wallet_address&mint=mint_address&amount=200&user_id=user123
|
||||
```
|
||||
|
||||
#### Get All Orders
|
||||
- **GET** `/api/v1/get_all_orders`
|
||||
|
||||
**Example:**
|
||||
```
|
||||
GET /api/v1/get_all_orders
|
||||
```
|
||||
|
||||
#### Get Order by ID
|
||||
- **GET** `/api/v1/get_order_by_id/:id`
|
||||
|
||||
**Example:**
|
||||
```
|
||||
GET /api/v1/get_order_by_id/1
|
||||
```
|
||||
|
||||
#### Get Orders by User ID
|
||||
- **GET** `/api/v1/get_orders_by_user/:userId`
|
||||
|
||||
**Example:**
|
||||
```
|
||||
GET /api/v1/get_orders_by_user/user123
|
||||
```
|
||||
|
||||
#### Update Order
|
||||
- **GET** `/api/v1/update_order/:id`
|
||||
- **Query Parameters:** (any combination of fields)
|
||||
- `wallet` (optional) - New wallet address
|
||||
- `mint` (optional) - New mint address
|
||||
- `amount` (optional) - New amount
|
||||
- `user_id` (optional) - New user ID
|
||||
|
||||
**Example:**
|
||||
```
|
||||
GET /api/v1/update_order/1?amount=300&user_id=newuser456
|
||||
```
|
||||
|
||||
#### Delete Order
|
||||
- **GET** `/api/v1/delete_order/:id`
|
||||
|
||||
**Example:**
|
||||
```
|
||||
GET /api/v1/delete_order/1
|
||||
```
|
||||
|
||||
### Health Check
|
||||
- **GET** `/health` - Returns API status and database connection status
|
||||
|
||||
## Response Format
|
||||
|
||||
All API responses follow this format:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {...},
|
||||
"count": 1
|
||||
}
|
||||
```
|
||||
|
||||
Error responses:
|
||||
```json
|
||||
{
|
||||
"error": "Error message",
|
||||
"message": "Detailed error description"
|
||||
}
|
||||
```
|
||||
|
||||
## Database Schema
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| id | INT | Auto-increment primary key |
|
||||
| wallet | TEXT | Wallet address |
|
||||
| mint | TEXT | Mint address |
|
||||
| amount | BIGINT | Order amount (default: 100) |
|
||||
| created_at | DATETIME | Timestamp (auto-generated) |
|
||||
| user_id | TEXT | User identifier |
|
||||
|
||||
## Development
|
||||
|
||||
- **TypeScript** for type safety
|
||||
- **Express.js** for the web framework
|
||||
- **MySQL2** for database operations
|
||||
- **Dotenv** for environment variable management
|
||||
2427
package-lock.json
generated
Normal file
2427
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
30
package.json
Normal file
30
package.json
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"name": "dmrocket_api",
|
||||
"version": "1.0.0",
|
||||
"main": "dist/app.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "node dist/app.js",
|
||||
"dev": "ts-node src/app.ts",
|
||||
"dev:watch": "nodemon --exec ts-node src/app.ts",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.1",
|
||||
"@types/node": "^20.0.0",
|
||||
"eslint": "^9.31.0",
|
||||
"nodemon": "^3.0.0",
|
||||
"ts-node": "^10.9.0",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/mysql": "^2.15.27",
|
||||
"dotenv": "^17.2.0",
|
||||
"express": "^4.17.1",
|
||||
"mysql2": "^3.14.2"
|
||||
}
|
||||
}
|
||||
61
src/app.ts
Normal file
61
src/app.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import express, { Request, Response } from 'express';
|
||||
import dotenv from 'dotenv';
|
||||
import { testConnection } from './config/database';
|
||||
import orderRoutes from './routes/orderRoutes';
|
||||
|
||||
// Load environment variables
|
||||
dotenv.config({ path: '.env.local' });
|
||||
|
||||
const app = express();
|
||||
const port: number = 7111;
|
||||
|
||||
// Middleware
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
|
||||
// Test database connection on startup
|
||||
testConnection().then((connected) => {
|
||||
if (!connected) {
|
||||
console.error('Failed to connect to database. Please check your .env.local configuration.');
|
||||
}
|
||||
});
|
||||
|
||||
// Routes
|
||||
app.get('/', (req: Request, res: Response) => {
|
||||
res.send('DMRocket API - Unauthorized');
|
||||
});
|
||||
|
||||
// API Routes
|
||||
app.use('/api/v1', orderRoutes);
|
||||
|
||||
// Health check endpoint
|
||||
app.get('/health', (req: Request, res: Response) => {
|
||||
res.json({
|
||||
status: 'OK',
|
||||
timestamp: new Date().toISOString(),
|
||||
database: 'Connected'
|
||||
});
|
||||
});
|
||||
|
||||
// Error handling middleware
|
||||
app.use((err: any, req: Request, res: Response, next: any) => {
|
||||
console.error('Error:', err);
|
||||
res.status(500).json({
|
||||
error: 'Internal server error',
|
||||
message: err.message || 'Something went wrong'
|
||||
});
|
||||
});
|
||||
|
||||
// 404 handler
|
||||
app.use('*', (req: Request, res: Response) => {
|
||||
res.status(404).json({
|
||||
error: 'Not found',
|
||||
message: 'The requested endpoint does not exist'
|
||||
});
|
||||
});
|
||||
|
||||
// Start server
|
||||
app.listen(port, () => {
|
||||
console.log(`DMRocket API is listening at http://localhost:${port}`);
|
||||
console.log(`Health check available at http://localhost:${port}/health`);
|
||||
});
|
||||
35
src/config/database.ts
Normal file
35
src/config/database.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import mysql from 'mysql2/promise';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
// Load environment variables
|
||||
dotenv.config({ path: '.env.local' });
|
||||
|
||||
// Database configuration
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'localhost',
|
||||
user: process.env.DB_USER || 'root',
|
||||
password: process.env.DB_PASSWORD || '',
|
||||
database: process.env.DB_NAME || 'dmrocket',
|
||||
port: parseInt(process.env.DB_PORT || '3306'),
|
||||
waitForConnections: true,
|
||||
connectionLimit: 10,
|
||||
queueLimit: 0
|
||||
};
|
||||
|
||||
// Create connection pool
|
||||
const pool = mysql.createPool(dbConfig);
|
||||
|
||||
// Test database connection
|
||||
export const testConnection = async () => {
|
||||
try {
|
||||
const connection = await pool.getConnection();
|
||||
console.log('Database connected successfully');
|
||||
connection.release();
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Database connection failed:', error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export default pool;
|
||||
196
src/routes/orderRoutes.ts
Normal file
196
src/routes/orderRoutes.ts
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
import { Router, Request, Response } from 'express';
|
||||
import { OrderService, Order } from '../services/orderService';
|
||||
|
||||
const router = Router();
|
||||
|
||||
// Create a new order (GET with query parameters)
|
||||
router.get('/create_order', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { wallet, mint, amount, user_id } = req.query;
|
||||
|
||||
// Validate required fields
|
||||
if (!wallet || !mint || !user_id) {
|
||||
return res.status(400).json({
|
||||
error: 'Missing required fields: wallet, mint, and user_id are required'
|
||||
});
|
||||
}
|
||||
|
||||
const orderData: Order = {
|
||||
wallet: wallet as string,
|
||||
mint: mint as string,
|
||||
amount: amount ? parseInt(amount as string) : 100, // Default to 100 if not provided
|
||||
user_id: user_id as string
|
||||
};
|
||||
|
||||
const newOrder = await OrderService.createOrder(orderData);
|
||||
|
||||
return res.status(201).json({
|
||||
success: true,
|
||||
data: newOrder
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error creating order:', error);
|
||||
return res.status(500).json({
|
||||
error: 'Internal server error',
|
||||
message: 'Failed to create order'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Get all orders
|
||||
router.get('/get_all_orders', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const orders = await OrderService.getAllOrders();
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
data: orders,
|
||||
count: orders.length
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error fetching orders:', error);
|
||||
return res.status(500).json({
|
||||
error: 'Internal server error',
|
||||
message: 'Failed to fetch orders'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Get order by ID
|
||||
router.get('/get_order_by_id/:id', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const id = parseInt(req.params.id as string);
|
||||
|
||||
if (isNaN(id)) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid order ID'
|
||||
});
|
||||
}
|
||||
|
||||
const order = await OrderService.getOrderById(id);
|
||||
|
||||
if (!order) {
|
||||
return res.status(404).json({
|
||||
error: 'Order not found'
|
||||
});
|
||||
}
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
data: order
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error fetching order:', error);
|
||||
return res.status(500).json({
|
||||
error: 'Internal server error',
|
||||
message: 'Failed to fetch order'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Get orders by user ID
|
||||
router.get('/get_orders_by_user/:userId', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { userId } = req.params;
|
||||
|
||||
if (!userId) {
|
||||
return res.status(400).json({
|
||||
error: 'User ID is required'
|
||||
});
|
||||
}
|
||||
|
||||
const orders = await OrderService.getOrdersByUserId(userId as string);
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
data: orders,
|
||||
count: orders.length
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error fetching user orders:', error);
|
||||
return res.status(500).json({
|
||||
error: 'Internal server error',
|
||||
message: 'Failed to fetch user orders'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Update order (GET with query parameters)
|
||||
router.get('/update_order/:id', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const id = parseInt(req.params.id as string);
|
||||
|
||||
if (isNaN(id)) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid order ID'
|
||||
});
|
||||
}
|
||||
|
||||
const { wallet, mint, amount, user_id } = req.query;
|
||||
const updates: Partial<Order> = {};
|
||||
|
||||
if (wallet !== undefined) updates.wallet = wallet as string;
|
||||
if (mint !== undefined) updates.mint = mint as string;
|
||||
if (amount !== undefined) updates.amount = parseInt(amount as string);
|
||||
if (user_id !== undefined) updates.user_id = user_id as string;
|
||||
|
||||
if (Object.keys(updates).length === 0) {
|
||||
return res.status(400).json({
|
||||
error: 'No valid fields to update'
|
||||
});
|
||||
}
|
||||
|
||||
const success = await OrderService.updateOrder(id, updates);
|
||||
|
||||
if (!success) {
|
||||
return res.status(404).json({
|
||||
error: 'Order not found'
|
||||
});
|
||||
}
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
message: 'Order updated successfully'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error updating order:', error);
|
||||
return res.status(500).json({
|
||||
error: 'Internal server error',
|
||||
message: 'Failed to update order'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Delete order
|
||||
router.get('/delete_order/:id', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const id = parseInt(req.params.id as string);
|
||||
|
||||
if (isNaN(id)) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid order ID'
|
||||
});
|
||||
}
|
||||
|
||||
const success = await OrderService.deleteOrder(id);
|
||||
|
||||
if (!success) {
|
||||
return res.status(404).json({
|
||||
error: 'Order not found'
|
||||
});
|
||||
}
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
message: 'Order deleted successfully'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error deleting order:', error);
|
||||
return res.status(500).json({
|
||||
error: 'Internal server error',
|
||||
message: 'Failed to delete order'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
115
src/services/orderService.ts
Normal file
115
src/services/orderService.ts
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
import pool from '../config/database';
|
||||
|
||||
export interface Order {
|
||||
id?: number;
|
||||
wallet: string;
|
||||
mint: string;
|
||||
amount: number;
|
||||
created_at?: Date;
|
||||
user_id: string;
|
||||
}
|
||||
|
||||
export class OrderService {
|
||||
// Create a new order
|
||||
static async createOrder(order: Order): Promise<Order> {
|
||||
try {
|
||||
const [result] = await pool.execute(
|
||||
'INSERT INTO Orders (wallet, mint, amount, user_id) VALUES (?, ?, ?, ?)',
|
||||
[order.wallet, order.mint, order.amount, order.user_id]
|
||||
);
|
||||
|
||||
const insertResult = result as any;
|
||||
const newOrder: Order = {
|
||||
id: insertResult.insertId,
|
||||
wallet: order.wallet,
|
||||
mint: order.mint,
|
||||
amount: order.amount,
|
||||
user_id: order.user_id,
|
||||
created_at: new Date()
|
||||
};
|
||||
|
||||
return newOrder;
|
||||
} catch (error) {
|
||||
console.error('Error creating order:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Get all orders
|
||||
static async getAllOrders(): Promise<Order[]> {
|
||||
try {
|
||||
const [rows] = await pool.execute('SELECT * FROM Orders ORDER BY created_at DESC');
|
||||
return rows as Order[];
|
||||
} catch (error) {
|
||||
console.error('Error fetching orders:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Get order by ID
|
||||
static async getOrderById(id: number): Promise<Order | null> {
|
||||
try {
|
||||
const [rows] = await pool.execute('SELECT * FROM Orders WHERE id = ?', [id]);
|
||||
const orders = rows as Order[];
|
||||
return orders.length > 0 ? orders[0] || null : null;
|
||||
} catch (error) {
|
||||
console.error('Error fetching order by ID:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Get orders by user_id
|
||||
static async getOrdersByUserId(userId: string): Promise<Order[]> {
|
||||
try {
|
||||
const [rows] = await pool.execute('SELECT * FROM Orders WHERE user_id = ? ORDER BY created_at DESC', [userId]);
|
||||
return rows as Order[];
|
||||
} catch (error) {
|
||||
console.error('Error fetching orders by user ID:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Update order
|
||||
static async updateOrder(id: number, updates: Partial<Order>): Promise<boolean> {
|
||||
try {
|
||||
const allowedFields = ['wallet', 'mint', 'amount', 'user_id'];
|
||||
const updateFields: string[] = [];
|
||||
const updateValues: any[] = [];
|
||||
|
||||
for (const [key, value] of Object.entries(updates)) {
|
||||
if (allowedFields.includes(key) && value !== undefined) {
|
||||
updateFields.push(`${key} = ?`);
|
||||
updateValues.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
if (updateFields.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
updateValues.push(id);
|
||||
const [result] = await pool.execute(
|
||||
`UPDATE Orders SET ${updateFields.join(', ')} WHERE id = ?`,
|
||||
updateValues
|
||||
);
|
||||
|
||||
const updateResult = result as any;
|
||||
return updateResult.affectedRows > 0;
|
||||
} catch (error) {
|
||||
console.error('Error updating order:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete order
|
||||
static async deleteOrder(id: number): Promise<boolean> {
|
||||
try {
|
||||
const [result] = await pool.execute('DELETE FROM Orders WHERE id = ?', [id]);
|
||||
const deleteResult = result as any;
|
||||
return deleteResult.affectedRows > 0;
|
||||
} catch (error) {
|
||||
console.error('Error deleting order:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
31
tsconfig.json
Normal file
31
tsconfig.json
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "commonjs",
|
||||
"lib": ["ES2020"],
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"removeComments": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"exactOptionalPropertyTypes": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user