Skip to main content

x402_cli/x402/
project.rs

1use anyhow::{Context, Result};
2use colored::Colorize;
3use serde::{Deserialize, Serialize};
4use std::fs;
5use std::path::PathBuf;
6use std::process::Command;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct Project {
10    pub name: String,
11    pub chain: String,
12    pub framework: String,
13    pub version: String,
14}
15
16impl Project {
17    pub fn new(name: String, chain: String, framework: String) -> Self {
18        let version = "0.1.0".to_string();
19        Project {
20            name,
21            chain,
22            framework,
23            version,
24        }
25    }
26
27    pub fn create_directories(&self) -> Result<()> {
28        let base_dir = PathBuf::from(&self.name);
29
30        let dirs = vec![
31            base_dir.join("src"),
32            base_dir.join("config"),
33            base_dir.join("tests"),
34            base_dir.join("docs"),
35        ];
36
37        for dir in dirs {
38            fs::create_dir_all(&dir)
39                .with_context(|| format!("Failed to create directory: {}", dir.display()))?;
40        }
41
42        println!(
43            "{}",
44            format!("  ✓ Created directories for {}", self.name.green()).dimmed()
45        );
46        Ok(())
47    }
48
49    pub fn create_config_files(&self) -> Result<()> {
50        let base_dir = PathBuf::from(&self.name);
51        let config_dir = base_dir.join("config");
52
53        let config_content = format!(
54            r#"# x402 Configuration
55project_name = "{}"
56chain = "{}"
57framework = "{}"
58version = "{}"
59
60[server]
61port = 3000
62host = "localhost"
63
64[blockchain]
65network = "{}"
66
67[facilitator]
68enabled = true
69port = 3001
70"#,
71            self.name, self.chain, self.framework, self.version, self.chain
72        );
73
74        fs::write(config_dir.join("x402.toml"), config_content)
75            .with_context(|| format!("Failed to create config file"))?;
76
77        let env_content = format!(
78            r#"# x402 Environment Variables
79NODE_ENV=development
80X402_CHAIN={}
81X402_PROJECT={}
82"#,
83            self.chain, self.name
84        );
85
86        fs::write(base_dir.join(".env.example"), env_content)
87            .with_context(|| format!("Failed to create .env.example"))?;
88
89        let gitignore_content = r#"# Dependencies
90node_modules/
91target/
92
93# Environment
94.env
95
96# Logs
97*.log
98npm-debug.log*
99
100# IDE
101.vscode/
102.idea/
103
104# Build
105dist/
106build/"#;
107
108        fs::write(base_dir.join(".gitignore"), gitignore_content)
109            .with_context(|| format!("Failed to create .gitignore"))?;
110
111        println!("{}", "  ✓ Created configuration files".dimmed());
112        Ok(())
113    }
114
115    pub fn install_dependencies(&self) -> Result<()> {
116        match self.framework.to_lowercase().as_str() {
117            "next" | "nextjs" => {
118                let output = Command::new("npm")
119                    .args(["init", "-y"])
120                    .current_dir(&self.name)
121                    .output()
122                    .context("Failed to run npm init")?;
123
124                if output.status.success() {
125                    println!("{}", "  ✓ Installed Node.js dependencies".dimmed());
126                } else {
127                    let error = String::from_utf8_lossy(&output.stderr);
128                    println!(
129                        "{}",
130                        format!("  ⚠ npm init warning: {}", error.trim())
131                            .yellow()
132                            .dimmed()
133                    );
134                }
135            }
136            "react" => {
137                println!(
138                    "{}",
139                    "  ⚠ React: Run `npm install` after project creation"
140                        .yellow()
141                        .dimmed()
142                );
143            }
144            _ => {
145                println!(
146                    "{}",
147                    "  ℹ Custom framework: Install dependencies manually".dimmed()
148                );
149            }
150        }
151
152        Ok(())
153    }
154
155    pub fn generate_readme(&self) -> Result<()> {
156        let readme_content = format!(
157            r#"# {} - {} Framework
158
159An x402-enabled API built on {} blockchain.
160
161## Features
162
163- Payment-enabled API endpoints
164- Automated wallet management
165- Development facilitator integration
166
167## Getting Started
168
169```bash
170# Install dependencies (if applicable)
171npm install
172
173# Copy environment variables
174cp .env.example .env
175
176# Run the development server
177npm run dev
178
179# Start the facilitator
180x402 facilitator start
181```
182
183## Configuration
184
185See `config/x402.toml` for project configuration.
186
187## x402 CLI Commands
188
189```bash
190# Initialize new x402-enabled API
191x402 init my-weather-api --chain aptos --framework next
192
193# Create a test wallet
194x402 wallet create --network testnet
195
196# Start local facilitator for testing
197x402 facilitator start --port 3001
198
199# Test a payment flow end-to-end
200x402 test payment --api http://localhost:3000/weather --amount 1000
201
202# Deploy to production
203x402 deploy --provider vercel
204```
205
206## Documentation
207
208See the `docs/` directory for additional documentation.
209"#,
210            self.name, self.framework, self.chain
211        );
212
213        fs::write(format!("{}/README.md", self.name), readme_content)
214            .with_context(|| format!("Failed to create README.md"))?;
215
216        println!("{}", "  ✓ Generated README.md".dimmed());
217        Ok(())
218    }
219}