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}