@mexty/cli 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +427 -0
- package/dist/commands/create.d.ts +7 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +80 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/delete.d.ts +2 -0
- package/dist/commands/delete.d.ts.map +1 -0
- package/dist/commands/delete.js +54 -0
- package/dist/commands/delete.js.map +1 -0
- package/dist/commands/fork.d.ts +2 -0
- package/dist/commands/fork.d.ts.map +1 -0
- package/dist/commands/fork.js +52 -0
- package/dist/commands/fork.js.map +1 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +12 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/publish.d.ts +2 -0
- package/dist/commands/publish.d.ts.map +1 -0
- package/dist/commands/publish.js +139 -0
- package/dist/commands/publish.js.map +1 -0
- package/dist/commands/sync.d.ts +2 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +140 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/api.d.ts +55 -0
- package/dist/utils/api.d.ts.map +1 -0
- package/dist/utils/api.js +68 -0
- package/dist/utils/api.js.map +1 -0
- package/dist/utils/git.d.ts +41 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +171 -0
- package/dist/utils/git.js.map +1 -0
- package/package.json +39 -0
- package/src/commands/create.ts +97 -0
- package/src/commands/delete.ts +63 -0
- package/src/commands/fork.ts +58 -0
- package/src/commands/login.ts +104 -0
- package/src/commands/publish.ts +159 -0
- package/src/commands/sync.ts +284 -0
- package/src/index.ts +84 -0
- package/src/utils/api.ts +240 -0
- package/src/utils/auth.ts +21 -0
- package/src/utils/git.ts +194 -0
- package/tsconfig.json +24 -0
@@ -0,0 +1,171 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.GitManager = void 0;
|
7
|
+
const simple_git_1 = __importDefault(require("simple-git"));
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
10
|
+
// Simple spinner implementation since ora v5 has import issues
|
11
|
+
class SimpleSpinner {
|
12
|
+
constructor(message) {
|
13
|
+
this.interval = null;
|
14
|
+
this.frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
15
|
+
this.currentFrame = 0;
|
16
|
+
this.message = message;
|
17
|
+
}
|
18
|
+
start() {
|
19
|
+
process.stdout.write(this.message);
|
20
|
+
this.interval = setInterval(() => {
|
21
|
+
process.stdout.write(`\r${this.frames[this.currentFrame]} ${this.message}`);
|
22
|
+
this.currentFrame = (this.currentFrame + 1) % this.frames.length;
|
23
|
+
}, 80);
|
24
|
+
return this;
|
25
|
+
}
|
26
|
+
succeed(message) {
|
27
|
+
this.stop();
|
28
|
+
console.log(`\r✅ ${message}`);
|
29
|
+
}
|
30
|
+
fail(message) {
|
31
|
+
this.stop();
|
32
|
+
console.log(`\r❌ ${message}`);
|
33
|
+
}
|
34
|
+
stop() {
|
35
|
+
if (this.interval) {
|
36
|
+
clearInterval(this.interval);
|
37
|
+
this.interval = null;
|
38
|
+
}
|
39
|
+
process.stdout.write('\r');
|
40
|
+
}
|
41
|
+
}
|
42
|
+
function ora(message) {
|
43
|
+
return new SimpleSpinner(message);
|
44
|
+
}
|
45
|
+
class GitManager {
|
46
|
+
constructor(cwd) {
|
47
|
+
this.git = (0, simple_git_1.default)(cwd);
|
48
|
+
}
|
49
|
+
/**
|
50
|
+
* Clone a repository to a local directory
|
51
|
+
*/
|
52
|
+
async cloneRepository(repoUrl, targetDir) {
|
53
|
+
const spinner = ora(`Cloning repository from ${repoUrl}...`).start();
|
54
|
+
try {
|
55
|
+
// Ensure target directory doesn't exist
|
56
|
+
if (fs_1.default.existsSync(targetDir)) {
|
57
|
+
spinner.fail(chalk_1.default.red(`Directory ${targetDir} already exists`));
|
58
|
+
throw new Error(`Directory ${targetDir} already exists`);
|
59
|
+
}
|
60
|
+
// Clone the repository
|
61
|
+
await this.git.clone(repoUrl, targetDir);
|
62
|
+
spinner.succeed(chalk_1.default.green(`Repository cloned to ${targetDir}`));
|
63
|
+
}
|
64
|
+
catch (error) {
|
65
|
+
spinner.fail(chalk_1.default.red(`Failed to clone repository: ${error.message}`));
|
66
|
+
throw error;
|
67
|
+
}
|
68
|
+
}
|
69
|
+
/**
|
70
|
+
* Check if current directory is a Git repository
|
71
|
+
*/
|
72
|
+
async isGitRepository(dir) {
|
73
|
+
try {
|
74
|
+
const git = dir ? (0, simple_git_1.default)(dir) : this.git;
|
75
|
+
await git.status();
|
76
|
+
return true;
|
77
|
+
}
|
78
|
+
catch (error) {
|
79
|
+
return false;
|
80
|
+
}
|
81
|
+
}
|
82
|
+
/**
|
83
|
+
* Get the current repository's remote URL
|
84
|
+
*/
|
85
|
+
async getRemoteUrl(dir) {
|
86
|
+
try {
|
87
|
+
const git = dir ? (0, simple_git_1.default)(dir) : this.git;
|
88
|
+
const remotes = await git.getRemotes(true);
|
89
|
+
const origin = remotes.find(remote => remote.name === 'origin');
|
90
|
+
return origin?.refs?.fetch || null;
|
91
|
+
}
|
92
|
+
catch (error) {
|
93
|
+
return null;
|
94
|
+
}
|
95
|
+
}
|
96
|
+
/**
|
97
|
+
* Check if there are uncommitted changes
|
98
|
+
*/
|
99
|
+
async hasUncommittedChanges(dir) {
|
100
|
+
try {
|
101
|
+
const git = dir ? (0, simple_git_1.default)(dir) : this.git;
|
102
|
+
const status = await git.status();
|
103
|
+
return !status.isClean();
|
104
|
+
}
|
105
|
+
catch (error) {
|
106
|
+
return false;
|
107
|
+
}
|
108
|
+
}
|
109
|
+
/**
|
110
|
+
* Push current branch to remote
|
111
|
+
*/
|
112
|
+
async pushToRemote(dir) {
|
113
|
+
const spinner = ora('Pushing changes to remote repository...').start();
|
114
|
+
try {
|
115
|
+
const git = dir ? (0, simple_git_1.default)(dir) : this.git;
|
116
|
+
// Get current branch
|
117
|
+
const status = await git.status();
|
118
|
+
const currentBranch = status.current;
|
119
|
+
if (!currentBranch) {
|
120
|
+
throw new Error('No current branch found');
|
121
|
+
}
|
122
|
+
// Push to remote
|
123
|
+
await git.push('origin', currentBranch);
|
124
|
+
spinner.succeed(chalk_1.default.green('Changes pushed to remote repository'));
|
125
|
+
}
|
126
|
+
catch (error) {
|
127
|
+
spinner.fail(chalk_1.default.red(`Failed to push changes: ${error.message}`));
|
128
|
+
throw error;
|
129
|
+
}
|
130
|
+
}
|
131
|
+
/**
|
132
|
+
* Get repository information
|
133
|
+
*/
|
134
|
+
async getRepositoryInfo(dir) {
|
135
|
+
const git = dir ? (0, simple_git_1.default)(dir) : this.git;
|
136
|
+
const status = await git.status();
|
137
|
+
const remoteUrl = await this.getRemoteUrl(dir);
|
138
|
+
return {
|
139
|
+
branch: status.current || 'unknown',
|
140
|
+
remoteUrl,
|
141
|
+
hasChanges: !status.isClean()
|
142
|
+
};
|
143
|
+
}
|
144
|
+
/**
|
145
|
+
* Extract repository name from URL
|
146
|
+
*/
|
147
|
+
static extractRepoName(gitUrl) {
|
148
|
+
// Handle both SSH and HTTPS URLs
|
149
|
+
const match = gitUrl.match(/\/([^\/]+?)(?:\.git)?$/);
|
150
|
+
if (match) {
|
151
|
+
return match[1];
|
152
|
+
}
|
153
|
+
// Fallback: use the last part of the URL
|
154
|
+
const parts = gitUrl.split('/');
|
155
|
+
return parts[parts.length - 1].replace('.git', '');
|
156
|
+
}
|
157
|
+
/**
|
158
|
+
* Validate Git URL format
|
159
|
+
*/
|
160
|
+
static isValidGitUrl(url) {
|
161
|
+
const patterns = [
|
162
|
+
/^https:\/\/github\.com\/[\w\-\.]+\/[\w\-\.]+(?:\.git)?$/,
|
163
|
+
/^git@github\.com:[\w\-\.]+\/[\w\-\.]+(?:\.git)?$/,
|
164
|
+
/^https:\/\/gitlab\.com\/[\w\-\.]+\/[\w\-\.]+(?:\.git)?$/,
|
165
|
+
/^git@gitlab\.com:[\w\-\.]+\/[\w\-\.]+(?:\.git)?$/
|
166
|
+
];
|
167
|
+
return patterns.some(pattern => pattern.test(url));
|
168
|
+
}
|
169
|
+
}
|
170
|
+
exports.GitManager = GitManager;
|
171
|
+
//# sourceMappingURL=git.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
{
|
2
|
+
"name": "@mexty/cli",
|
3
|
+
"version": "1.0.1",
|
4
|
+
"description": "MEXT CLI for managing blocks and repositories",
|
5
|
+
"main": "dist/index.js",
|
6
|
+
"bin": {
|
7
|
+
"mexty": "./dist/index.js"
|
8
|
+
},
|
9
|
+
"scripts": {
|
10
|
+
"build": "tsc",
|
11
|
+
"dev": "tsc --watch",
|
12
|
+
"start": "node dist/index.js"
|
13
|
+
},
|
14
|
+
"dependencies": {
|
15
|
+
"commander": "^11.1.0",
|
16
|
+
"axios": "^1.6.0",
|
17
|
+
"chalk": "^4.1.2",
|
18
|
+
"simple-git": "^3.20.0"
|
19
|
+
},
|
20
|
+
"devDependencies": {
|
21
|
+
"@types/node": "^20.0.0",
|
22
|
+
"typescript": "^5.0.0"
|
23
|
+
},
|
24
|
+
"engines": {
|
25
|
+
"node": ">=16.0.0"
|
26
|
+
},
|
27
|
+
"keywords": [
|
28
|
+
"mext",
|
29
|
+
"cli",
|
30
|
+
"blocks",
|
31
|
+
"federation",
|
32
|
+
"webpack"
|
33
|
+
],
|
34
|
+
"author": "MEXT Team",
|
35
|
+
"license": "MIT",
|
36
|
+
"publishConfig": {
|
37
|
+
"access": "public"
|
38
|
+
}
|
39
|
+
}
|
@@ -0,0 +1,97 @@
|
|
1
|
+
import chalk from 'chalk';
|
2
|
+
import path from 'path';
|
3
|
+
import { apiClient, CreateBlockRequest } from '../utils/api';
|
4
|
+
import { GitManager } from '../utils/git';
|
5
|
+
import { createInterface } from 'readline';
|
6
|
+
import { requireAuthentication, getAuthenticatedUser } from '../utils/auth';
|
7
|
+
|
8
|
+
interface CreateOptions {
|
9
|
+
description?: string;
|
10
|
+
type?: string;
|
11
|
+
}
|
12
|
+
|
13
|
+
// Simple prompt function to replace inquirer
|
14
|
+
async function prompt(question: string, defaultValue?: string): Promise<string> {
|
15
|
+
return new Promise((resolve) => {
|
16
|
+
const rl = createInterface({
|
17
|
+
input: process.stdin,
|
18
|
+
output: process.stdout
|
19
|
+
});
|
20
|
+
|
21
|
+
const promptText = defaultValue ? `${question} (${defaultValue}): ` : `${question}: `;
|
22
|
+
|
23
|
+
rl.question(promptText, (answer) => {
|
24
|
+
rl.close();
|
25
|
+
resolve(answer.trim() || defaultValue || '');
|
26
|
+
});
|
27
|
+
});
|
28
|
+
}
|
29
|
+
|
30
|
+
export async function createCommand(name: string, options: CreateOptions): Promise<void> {
|
31
|
+
try {
|
32
|
+
// Check authentication first
|
33
|
+
requireAuthentication();
|
34
|
+
|
35
|
+
const user = getAuthenticatedUser();
|
36
|
+
console.log(chalk.blue(`🚀 Creating new block: ${name}`));
|
37
|
+
console.log(chalk.gray(` User: ${user?.fullName || user?.email || 'Unknown'}`));
|
38
|
+
|
39
|
+
// Get description if not provided
|
40
|
+
let description = options.description;
|
41
|
+
if (!description) {
|
42
|
+
description = await prompt('Enter a description for the block', `Custom block: ${name}`);
|
43
|
+
}
|
44
|
+
|
45
|
+
// Prepare block data
|
46
|
+
const blockData: CreateBlockRequest = {
|
47
|
+
blockType: options.type || 'custom',
|
48
|
+
title: name,
|
49
|
+
description: description,
|
50
|
+
allowedBrickTypes: ['text', 'image', 'video', 'code', 'quiz'], // Default allowed types
|
51
|
+
scope: ['user-store'], // Default scope for CLI-created blocks
|
52
|
+
content: []
|
53
|
+
};
|
54
|
+
|
55
|
+
console.log(chalk.yellow('📡 Creating block on server...'));
|
56
|
+
|
57
|
+
// Create the block
|
58
|
+
const block = await apiClient.createBlock(blockData);
|
59
|
+
|
60
|
+
console.log(chalk.green(`✅ Block created successfully!`));
|
61
|
+
console.log(chalk.gray(` Block ID: ${block._id}`));
|
62
|
+
console.log(chalk.gray(` Block Type: ${block.blockType}`));
|
63
|
+
|
64
|
+
if (block.gitUrl) {
|
65
|
+
console.log(chalk.gray(` GitHub URL: ${block.gitUrl}`));
|
66
|
+
|
67
|
+
// Clone the repository
|
68
|
+
const repoName = GitManager.extractRepoName(block.gitUrl);
|
69
|
+
const targetDir = path.join(process.cwd(), repoName);
|
70
|
+
|
71
|
+
console.log(chalk.yellow(`📦 Cloning repository to ./${repoName}...`));
|
72
|
+
|
73
|
+
try {
|
74
|
+
const gitManager = new GitManager();
|
75
|
+
await gitManager.cloneRepository(block.gitUrl, targetDir);
|
76
|
+
|
77
|
+
console.log(chalk.green(`🎉 Block created and repository cloned successfully!`));
|
78
|
+
console.log(chalk.blue(`\nNext steps:`));
|
79
|
+
console.log(chalk.gray(` 1. cd ${repoName}`));
|
80
|
+
console.log(chalk.gray(` 2. Make your changes`));
|
81
|
+
console.log(chalk.gray(` 3. git add . && git commit -m "Your changes"`));
|
82
|
+
console.log(chalk.gray(` 4. mexty publish`));
|
83
|
+
|
84
|
+
} catch (cloneError: any) {
|
85
|
+
console.error(chalk.red(`❌ Failed to clone repository: ${cloneError.message}`));
|
86
|
+
console.log(chalk.yellow(`You can manually clone it later:`));
|
87
|
+
console.log(chalk.gray(` git clone ${block.gitUrl}`));
|
88
|
+
}
|
89
|
+
} else {
|
90
|
+
console.log(chalk.yellow('⚠️ No GitHub repository was created (GitHub not configured)'));
|
91
|
+
}
|
92
|
+
|
93
|
+
} catch (error: any) {
|
94
|
+
console.error(chalk.red(`❌ Failed to create block: ${error.message}`));
|
95
|
+
process.exit(1);
|
96
|
+
}
|
97
|
+
}
|
@@ -0,0 +1,63 @@
|
|
1
|
+
import chalk from 'chalk';
|
2
|
+
import { apiClient } from '../utils/api';
|
3
|
+
import { createInterface } from 'readline';
|
4
|
+
import { requireAuthentication, getAuthenticatedUser } from '../utils/auth';
|
5
|
+
|
6
|
+
// Simple confirmation function
|
7
|
+
async function confirm(question: string): Promise<boolean> {
|
8
|
+
return new Promise((resolve) => {
|
9
|
+
const rl = createInterface({
|
10
|
+
input: process.stdin,
|
11
|
+
output: process.stdout
|
12
|
+
});
|
13
|
+
|
14
|
+
rl.question(`${question} (y/N): `, (answer) => {
|
15
|
+
rl.close();
|
16
|
+
resolve(answer.toLowerCase().trim() === 'y' || answer.toLowerCase().trim() === 'yes');
|
17
|
+
});
|
18
|
+
});
|
19
|
+
}
|
20
|
+
|
21
|
+
export async function deleteCommand(blockId: string): Promise<void> {
|
22
|
+
try {
|
23
|
+
// Check authentication first
|
24
|
+
requireAuthentication();
|
25
|
+
|
26
|
+
const user = getAuthenticatedUser();
|
27
|
+
console.log(chalk.blue(`🗑️ Deleting block: ${blockId}`));
|
28
|
+
console.log(chalk.gray(` User: ${user?.fullName || user?.email || 'Unknown'}`));
|
29
|
+
|
30
|
+
// Get block info first
|
31
|
+
console.log(chalk.yellow('📡 Fetching block information...'));
|
32
|
+
const block = await apiClient.getBlock(blockId);
|
33
|
+
|
34
|
+
console.log(chalk.gray(` Title: ${block.title}`));
|
35
|
+
console.log(chalk.gray(` Description: ${block.description}`));
|
36
|
+
if (block.gitUrl) {
|
37
|
+
console.log(chalk.gray(` GitHub URL: ${block.gitUrl}`));
|
38
|
+
}
|
39
|
+
|
40
|
+
// Confirm deletion
|
41
|
+
const confirmed = await confirm(chalk.red('Are you sure you want to delete this block? This action cannot be undone.'));
|
42
|
+
|
43
|
+
if (!confirmed) {
|
44
|
+
console.log(chalk.yellow('🚫 Deletion cancelled.'));
|
45
|
+
return;
|
46
|
+
}
|
47
|
+
|
48
|
+
// Delete the block
|
49
|
+
console.log(chalk.yellow('📡 Deleting block on server...'));
|
50
|
+
await apiClient.deleteBlock(blockId);
|
51
|
+
|
52
|
+
console.log(chalk.green(`✅ Block deleted successfully!`));
|
53
|
+
|
54
|
+
if (block.gitUrl) {
|
55
|
+
console.log(chalk.yellow('⚠️ Note: The GitHub repository still exists and needs to be deleted manually if desired.'));
|
56
|
+
console.log(chalk.gray(` Repository: ${block.gitUrl}`));
|
57
|
+
}
|
58
|
+
|
59
|
+
} catch (error: any) {
|
60
|
+
console.error(chalk.red(`❌ Failed to delete block: ${error.message}`));
|
61
|
+
process.exit(1);
|
62
|
+
}
|
63
|
+
}
|
@@ -0,0 +1,58 @@
|
|
1
|
+
import chalk from 'chalk';
|
2
|
+
import path from 'path';
|
3
|
+
import { apiClient } from '../utils/api';
|
4
|
+
import { GitManager } from '../utils/git';
|
5
|
+
import { requireAuthentication, getAuthenticatedUser } from '../utils/auth';
|
6
|
+
|
7
|
+
export async function forkCommand(blockId: string): Promise<void> {
|
8
|
+
try {
|
9
|
+
// Check authentication first
|
10
|
+
requireAuthentication();
|
11
|
+
|
12
|
+
const user = getAuthenticatedUser();
|
13
|
+
console.log(chalk.blue(`🍴 Forking block: ${blockId}`));
|
14
|
+
console.log(chalk.gray(` User: ${user?.fullName || user?.email || 'Unknown'}`));
|
15
|
+
|
16
|
+
// Fork the block
|
17
|
+
console.log(chalk.yellow('📡 Forking block on server...'));
|
18
|
+
const forkedBlock = await apiClient.forkBlock({ blockId });
|
19
|
+
|
20
|
+
console.log(chalk.green(`✅ Block forked successfully!`));
|
21
|
+
console.log(chalk.gray(` New Block ID: ${forkedBlock._id}`));
|
22
|
+
console.log(chalk.gray(` Title: ${forkedBlock.title}`));
|
23
|
+
console.log(chalk.gray(` Description: ${forkedBlock.description}`));
|
24
|
+
|
25
|
+
if (forkedBlock.gitUrl) {
|
26
|
+
console.log(chalk.gray(` GitHub URL: ${forkedBlock.gitUrl}`));
|
27
|
+
|
28
|
+
// Clone the forked repository
|
29
|
+
const repoName = GitManager.extractRepoName(forkedBlock.gitUrl);
|
30
|
+
const targetDir = path.join(process.cwd(), repoName);
|
31
|
+
|
32
|
+
console.log(chalk.yellow(`📦 Cloning forked repository to ./${repoName}...`));
|
33
|
+
|
34
|
+
try {
|
35
|
+
const gitManager = new GitManager();
|
36
|
+
await gitManager.cloneRepository(forkedBlock.gitUrl, targetDir);
|
37
|
+
|
38
|
+
console.log(chalk.green(`🎉 Block forked and repository cloned successfully!`));
|
39
|
+
console.log(chalk.blue(`\nNext steps:`));
|
40
|
+
console.log(chalk.gray(` 1. cd ${repoName}`));
|
41
|
+
console.log(chalk.gray(` 2. Make your changes`));
|
42
|
+
console.log(chalk.gray(` 3. git add . && git commit -m "Your changes"`));
|
43
|
+
console.log(chalk.gray(` 4. mexty publish`));
|
44
|
+
|
45
|
+
} catch (cloneError: any) {
|
46
|
+
console.error(chalk.red(`❌ Failed to clone repository: ${cloneError.message}`));
|
47
|
+
console.log(chalk.yellow(`You can manually clone it later:`));
|
48
|
+
console.log(chalk.gray(` git clone ${forkedBlock.gitUrl}`));
|
49
|
+
}
|
50
|
+
} else {
|
51
|
+
console.log(chalk.yellow('⚠️ No GitHub repository available for this block'));
|
52
|
+
}
|
53
|
+
|
54
|
+
} catch (error: any) {
|
55
|
+
console.error(chalk.red(`❌ Failed to fork block: ${error.message}`));
|
56
|
+
process.exit(1);
|
57
|
+
}
|
58
|
+
}
|
@@ -0,0 +1,104 @@
|
|
1
|
+
import chalk from 'chalk';
|
2
|
+
import { createInterface } from 'readline';
|
3
|
+
import { apiClient } from '../utils/api';
|
4
|
+
|
5
|
+
// Simple prompt function
|
6
|
+
async function prompt(question: string): Promise<string> {
|
7
|
+
return new Promise((resolve) => {
|
8
|
+
const rl = createInterface({
|
9
|
+
input: process.stdin,
|
10
|
+
output: process.stdout
|
11
|
+
});
|
12
|
+
|
13
|
+
rl.question(question, (answer) => {
|
14
|
+
rl.close();
|
15
|
+
resolve(answer.trim());
|
16
|
+
});
|
17
|
+
});
|
18
|
+
}
|
19
|
+
|
20
|
+
// Wait function for countdown
|
21
|
+
async function wait(seconds: number): Promise<void> {
|
22
|
+
return new Promise(resolve => setTimeout(resolve, seconds * 1000));
|
23
|
+
}
|
24
|
+
|
25
|
+
export async function loginCommand(): Promise<void> {
|
26
|
+
try {
|
27
|
+
console.log(chalk.blue('🔐 Login to MEXT'));
|
28
|
+
|
29
|
+
// Check if already authenticated
|
30
|
+
if (apiClient.isAuthenticated()) {
|
31
|
+
const user = apiClient.getStoredUser();
|
32
|
+
console.log(chalk.green('✅ You are already logged in!'));
|
33
|
+
console.log(chalk.gray(` Email: ${user?.email || 'Unknown'}`));
|
34
|
+
console.log(chalk.gray(` Name: ${user?.fullName || 'Not set'}`));
|
35
|
+
|
36
|
+
const logout = await prompt('Do you want to logout and login as a different user? (y/N): ');
|
37
|
+
if (logout.toLowerCase() !== 'y' && logout.toLowerCase() !== 'yes') {
|
38
|
+
return;
|
39
|
+
}
|
40
|
+
|
41
|
+
await apiClient.logout();
|
42
|
+
console.log(chalk.yellow('📤 Logged out successfully'));
|
43
|
+
}
|
44
|
+
|
45
|
+
// Request email
|
46
|
+
const email = await prompt('Enter your email address: ');
|
47
|
+
|
48
|
+
if (!email || !email.includes('@')) {
|
49
|
+
console.error(chalk.red('❌ Please provide a valid email address'));
|
50
|
+
process.exit(1);
|
51
|
+
}
|
52
|
+
|
53
|
+
console.log(chalk.yellow('📧 Requesting verification code...'));
|
54
|
+
|
55
|
+
// Request OTP
|
56
|
+
try {
|
57
|
+
const otpResponse = await apiClient.requestOTP(email);
|
58
|
+
|
59
|
+
if (!otpResponse.success) {
|
60
|
+
console.error(chalk.red(`❌ ${otpResponse.message}`));
|
61
|
+
process.exit(1);
|
62
|
+
}
|
63
|
+
|
64
|
+
console.log(chalk.green('✅ Verification code sent to your email'));
|
65
|
+
console.log(chalk.gray(' Please check your inbox (and spam folder)'));
|
66
|
+
|
67
|
+
// Wait a moment for the user to check email
|
68
|
+
await wait(2);
|
69
|
+
|
70
|
+
// Request OTP code
|
71
|
+
const otp = await prompt('Enter the 6-digit verification code: ');
|
72
|
+
|
73
|
+
if (!otp || otp.length !== 6 || !/^\d{6}$/.test(otp)) {
|
74
|
+
console.error(chalk.red('❌ Please provide a valid 6-digit code'));
|
75
|
+
process.exit(1);
|
76
|
+
}
|
77
|
+
|
78
|
+
console.log(chalk.yellow('🔓 Verifying code...'));
|
79
|
+
|
80
|
+
// Verify OTP
|
81
|
+
const verifyResponse = await apiClient.verifyOTP(email, otp);
|
82
|
+
|
83
|
+
if (!verifyResponse.success) {
|
84
|
+
console.error(chalk.red(`❌ ${verifyResponse.message}`));
|
85
|
+
process.exit(1);
|
86
|
+
}
|
87
|
+
|
88
|
+
console.log(chalk.green('🎉 Login successful!'));
|
89
|
+
console.log(chalk.gray(` Welcome, ${verifyResponse.user?.fullName || verifyResponse.user?.email || 'User'}!`));
|
90
|
+
|
91
|
+
if (!verifyResponse.user?.isProfileComplete) {
|
92
|
+
console.log(chalk.yellow('⚠️ Your profile is incomplete. Please complete it in the web interface.'));
|
93
|
+
}
|
94
|
+
|
95
|
+
} catch (error: any) {
|
96
|
+
console.error(chalk.red(`❌ Login failed: ${error.message}`));
|
97
|
+
process.exit(1);
|
98
|
+
}
|
99
|
+
|
100
|
+
} catch (error: any) {
|
101
|
+
console.error(chalk.red(`❌ Login error: ${error.message}`));
|
102
|
+
process.exit(1);
|
103
|
+
}
|
104
|
+
}
|