lumbung-cli 1.0.0
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 +255 -0
- package/bin/lumbung.js +118 -0
- package/package.json +38 -0
- package/src/commands/auth.js +95 -0
- package/src/commands/get.js +74 -0
- package/src/commands/list.js +72 -0
- package/src/commands/push.js +110 -0
- package/src/commands/search.js +64 -0
- package/src/lib/api.js +142 -0
- package/src/lib/config.js +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# 🖥️ lumbung CLI
|
|
2
|
+
|
|
3
|
+
Command Line Interface untuk **lumbung - Modern Snippet Manager**.
|
|
4
|
+
|
|
5
|
+
Upload, cari, dan kelola snippet kode langsung dari terminal Anda.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 📦 Instalasi
|
|
10
|
+
|
|
11
|
+
### Prasyarat
|
|
12
|
+
- Node.js versi 18 atau lebih baru
|
|
13
|
+
- Akun lumbung (daftar di https://lumbungkode.netlify.app)
|
|
14
|
+
|
|
15
|
+
### Install dari Source
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Clone repository
|
|
19
|
+
cd snippet-manager/cli
|
|
20
|
+
|
|
21
|
+
# Install dependencies
|
|
22
|
+
npm install
|
|
23
|
+
|
|
24
|
+
# Link secara global
|
|
25
|
+
npm link
|
|
26
|
+
|
|
27
|
+
# Sekarang bisa digunakan dari mana saja
|
|
28
|
+
lumbung --help
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 🚀 Panduan Penggunaan
|
|
34
|
+
|
|
35
|
+
### 1. Login ke Akun Anda
|
|
36
|
+
|
|
37
|
+
Sebelum menggunakan CLI, login terlebih dahulu:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
lumbung login
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Masukkan email dan password akun lumbung Anda. Kredensial akan disimpan secara lokal.
|
|
44
|
+
|
|
45
|
+
Cek status login:
|
|
46
|
+
```bash
|
|
47
|
+
lumbung whoami
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 2. Upload Snippet dari File
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# Upload file dengan auto-detect title dan language
|
|
54
|
+
lumbung push myfile.js
|
|
55
|
+
|
|
56
|
+
# Upload dengan title custom
|
|
57
|
+
lumbung push script.py --title "Python Script"
|
|
58
|
+
|
|
59
|
+
# Upload sebagai public snippet
|
|
60
|
+
lumbung push helper.ts --public
|
|
61
|
+
|
|
62
|
+
# Upload dengan metadata lengkap
|
|
63
|
+
lumbung push api.go \
|
|
64
|
+
--title "Go REST API" \
|
|
65
|
+
--description "Simple REST endpoint" \
|
|
66
|
+
--tags "go,rest,api" \
|
|
67
|
+
--public
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 3. Lihat Daftar Snippet Anda
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# List semua snippet
|
|
74
|
+
lumbung list
|
|
75
|
+
|
|
76
|
+
# Filter by language
|
|
77
|
+
lumbung list --language python
|
|
78
|
+
|
|
79
|
+
# Limit hasil
|
|
80
|
+
lumbung list --limit 5
|
|
81
|
+
|
|
82
|
+
# Output sebagai JSON
|
|
83
|
+
lumbung list --json
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 4. Ambil Snippet by ID
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# Tampilkan snippet di terminal
|
|
90
|
+
lumbung get abc123
|
|
91
|
+
|
|
92
|
+
# Simpan ke file
|
|
93
|
+
lumbung get abc123 --output ./download/snippet.js
|
|
94
|
+
|
|
95
|
+
# Copy ke clipboard
|
|
96
|
+
lumbung get abc123 --copy
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 5. Cari Snippet
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# Search dengan kata kunci
|
|
103
|
+
lumbung search "react hooks"
|
|
104
|
+
|
|
105
|
+
# Search hanya snippet milik sendiri
|
|
106
|
+
lumbung search "useEffect" --mine
|
|
107
|
+
|
|
108
|
+
# Search snippet public
|
|
109
|
+
lumbung search "authentication" --public
|
|
110
|
+
|
|
111
|
+
# Filter by language
|
|
112
|
+
lumbung search "api" --language python
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 6. Logout
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
lumbung logout
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 📋 Referensi Command
|
|
124
|
+
|
|
125
|
+
| Command | Deskripsi |
|
|
126
|
+
|---------|-----------|
|
|
127
|
+
| `lumbung login` | Login ke akun lumbung |
|
|
128
|
+
| `lumbung logout` | Logout dari akun |
|
|
129
|
+
| `lumbung whoami` | Tampilkan user yang sedang login |
|
|
130
|
+
| `lumbung push <file>` | Upload file sebagai snippet |
|
|
131
|
+
| `lumbung get <id>` | Ambil snippet berdasarkan ID |
|
|
132
|
+
| `lumbung list` | Daftar snippet milik Anda |
|
|
133
|
+
| `lumbung search <query>` | Cari snippet dengan full-text search |
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 🎯 Options
|
|
138
|
+
|
|
139
|
+
### `push` Options
|
|
140
|
+
| Option | Deskripsi |
|
|
141
|
+
|--------|-----------|
|
|
142
|
+
| `-t, --title <title>` | Judul snippet |
|
|
143
|
+
| `-l, --language <lang>` | Bahasa pemrograman |
|
|
144
|
+
| `-d, --description <desc>` | Deskripsi snippet |
|
|
145
|
+
| `--tags <tags>` | Tags (pisahkan dengan koma) |
|
|
146
|
+
| `--public` | Jadikan snippet public |
|
|
147
|
+
|
|
148
|
+
### `get` Options
|
|
149
|
+
| Option | Deskripsi |
|
|
150
|
+
|--------|-----------|
|
|
151
|
+
| `-o, --output <file>` | Simpan ke file |
|
|
152
|
+
| `-c, --copy` | Copy ke clipboard |
|
|
153
|
+
|
|
154
|
+
### `list` Options
|
|
155
|
+
| Option | Deskripsi |
|
|
156
|
+
|--------|-----------|
|
|
157
|
+
| `-l, --language <lang>` | Filter by language |
|
|
158
|
+
| `-n, --limit <num>` | Limit hasil (default: 10) |
|
|
159
|
+
| `--public` | Hanya snippet public |
|
|
160
|
+
| `--json` | Output sebagai JSON |
|
|
161
|
+
|
|
162
|
+
### `search` Options
|
|
163
|
+
| Option | Deskripsi |
|
|
164
|
+
|--------|-----------|
|
|
165
|
+
| `-l, --language <lang>` | Filter by language |
|
|
166
|
+
| `-n, --limit <num>` | Limit hasil (default: 10) |
|
|
167
|
+
| `--mine` | Hanya snippet milik sendiri |
|
|
168
|
+
| `--public` | Hanya snippet public |
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 🌐 Bahasa yang Didukung
|
|
173
|
+
|
|
174
|
+
CLI mendukung **semua bahasa pemrograman**. Auto-detect berdasarkan extension:
|
|
175
|
+
|
|
176
|
+
| Extension | Language |
|
|
177
|
+
|-----------|----------|
|
|
178
|
+
| `.js`, `.jsx` | JavaScript |
|
|
179
|
+
| `.ts`, `.tsx` | TypeScript |
|
|
180
|
+
| `.py` | Python |
|
|
181
|
+
| `.java` | Java |
|
|
182
|
+
| `.cpp`, `.c` | C/C++ |
|
|
183
|
+
| `.go` | Go |
|
|
184
|
+
| `.rs` | Rust |
|
|
185
|
+
| `.rb` | Ruby |
|
|
186
|
+
| `.php` | PHP |
|
|
187
|
+
| `.html` | HTML |
|
|
188
|
+
| `.css`, `.scss` | CSS |
|
|
189
|
+
| `.sql` | SQL |
|
|
190
|
+
| `.md` | Markdown |
|
|
191
|
+
| `.json` | JSON |
|
|
192
|
+
| `.yaml`, `.yml` | YAML |
|
|
193
|
+
| Dan lainnya... | Auto-detect |
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## 💡 Contoh Penggunaan
|
|
198
|
+
|
|
199
|
+
### Workflow Harian
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
# Pagi: Login
|
|
203
|
+
lumbung login
|
|
204
|
+
|
|
205
|
+
# Simpan function baru yang dibuat
|
|
206
|
+
lumbung push ./src/utils/debounce.js --tags "utility,hooks"
|
|
207
|
+
|
|
208
|
+
# Cari snippet lama
|
|
209
|
+
lumbung search "validation"
|
|
210
|
+
|
|
211
|
+
# Ambil snippet dan gunakan
|
|
212
|
+
lumbung get xyz789 --output ./temp/snippet.js
|
|
213
|
+
|
|
214
|
+
# Sore: Check semua snippet hari ini
|
|
215
|
+
lumbung list --limit 20
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Scripting & Automation
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
# Export semua snippet sebagai JSON
|
|
222
|
+
lumbung list --json > my-snippets.json
|
|
223
|
+
|
|
224
|
+
# Batch upload
|
|
225
|
+
for file in ./src/**/*.js; do
|
|
226
|
+
lumbung push "$file" --public
|
|
227
|
+
done
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## ❓ Troubleshooting
|
|
233
|
+
|
|
234
|
+
### "Not logged in"
|
|
235
|
+
Jalankan `lumbung login` terlebih dahulu.
|
|
236
|
+
|
|
237
|
+
### "Snippet not found"
|
|
238
|
+
Pastikan ID snippet benar. Gunakan `lumbung list` untuk melihat ID snippet Anda.
|
|
239
|
+
|
|
240
|
+
### "Search failed"
|
|
241
|
+
Query minimal 3 karakter. Pastikan koneksi internet aktif.
|
|
242
|
+
|
|
243
|
+
### Credential issues
|
|
244
|
+
Hapus credential dan login ulang:
|
|
245
|
+
```bash
|
|
246
|
+
lumbung logout
|
|
247
|
+
lumbung login
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## 📄 License
|
|
253
|
+
|
|
254
|
+
MIT License - lumbung Team
|
|
255
|
+
|
package/bin/lumbung.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lumbung CLI - Command Line Interface for Lumbung Kode
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* lumbung login Login to your account
|
|
8
|
+
* lumbung push <file> Upload a snippet from file
|
|
9
|
+
* lumbung get <id> Fetch a snippet by ID
|
|
10
|
+
* lumbung list List your snippets
|
|
11
|
+
* lumbung search <query> Search snippets
|
|
12
|
+
* lumbung logout Logout from your account
|
|
13
|
+
*
|
|
14
|
+
* Run 'lumbung --help' for more information.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { program } from 'commander'
|
|
18
|
+
import chalk from 'chalk'
|
|
19
|
+
import { login, logout } from '../src/commands/auth.js'
|
|
20
|
+
import { push } from '../src/commands/push.js'
|
|
21
|
+
import { get } from '../src/commands/get.js'
|
|
22
|
+
import { list } from '../src/commands/list.js'
|
|
23
|
+
import { search } from '../src/commands/search.js'
|
|
24
|
+
import { getConfig } from '../src/lib/config.js'
|
|
25
|
+
|
|
26
|
+
// ASCII Art Banner
|
|
27
|
+
const banner = `
|
|
28
|
+
╔════════════════════════════════════════════════════════════════════════════════╗
|
|
29
|
+
║ ║
|
|
30
|
+
║ ██╗ ██╗ ██╗███╗ ███╗██████╗ ██╗ ██╗███╗ ██╗ ██████╗ ║
|
|
31
|
+
║ ██║ ██║ ██║████╗ ████║██╔══██╗██║ ██║████╗ ██║██╔════╝ ║
|
|
32
|
+
║ ██║ ██║ ██║██╔████╔██║██████╔╝██║ ██║██╔██╗ ██║██║ ███╗ ║
|
|
33
|
+
║ ██║ ██║ ██║██║╚██╔╝██║██╔══██╗██║ ██║██║╚██╗██║██║ ██║ ║
|
|
34
|
+
║ ███████╗╚██████╔╝██║ ╚═╝ ██║██████╔╝╚██████╔╝██║ ╚████║╚██████╔╝ ║
|
|
35
|
+
║ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚═╝ ╚═══╝ ╚══════╝ ║
|
|
36
|
+
║ ║
|
|
37
|
+
║ Gudang Snippet Kode Modern - CLI Tool ║
|
|
38
|
+
║ ║
|
|
39
|
+
╚════════════════════════════════════════════════════════════════════════════════╝
|
|
40
|
+
`
|
|
41
|
+
|
|
42
|
+
program
|
|
43
|
+
.name('lumbung')
|
|
44
|
+
.description('CLI tool for Lumbung Kode - Gudang Snippet Kode Modern')
|
|
45
|
+
.version('1.0.0')
|
|
46
|
+
.hook('preAction', (thisCommand) => {
|
|
47
|
+
// Only show banner for main help
|
|
48
|
+
if (thisCommand.args.length === 0 && !process.argv.slice(2).length) {
|
|
49
|
+
console.log(chalk.cyan(banner))
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// Login command
|
|
54
|
+
program
|
|
55
|
+
.command('login')
|
|
56
|
+
.description('Login to your Lumbung Kode account')
|
|
57
|
+
.action(login)
|
|
58
|
+
|
|
59
|
+
// Logout command
|
|
60
|
+
program
|
|
61
|
+
.command('logout')
|
|
62
|
+
.description('Logout from your account')
|
|
63
|
+
.action(logout)
|
|
64
|
+
|
|
65
|
+
// Push command
|
|
66
|
+
program
|
|
67
|
+
.command('push <file>')
|
|
68
|
+
.description('Upload a snippet from a file')
|
|
69
|
+
.option('-t, --title <title>', 'Snippet title (auto-detected if not provided)')
|
|
70
|
+
.option('-l, --language <language>', 'Programming language (auto-detected from extension)')
|
|
71
|
+
.option('-d, --description <description>', 'Snippet description')
|
|
72
|
+
.option('--tags <tags>', 'Comma-separated tags')
|
|
73
|
+
.option('--public', 'Make snippet public', false)
|
|
74
|
+
.action(push)
|
|
75
|
+
|
|
76
|
+
// Get command
|
|
77
|
+
program
|
|
78
|
+
.command('get <id>')
|
|
79
|
+
.description('Fetch a snippet by ID')
|
|
80
|
+
.option('-o, --output <file>', 'Save to file')
|
|
81
|
+
.option('-c, --copy', 'Copy to clipboard')
|
|
82
|
+
.action(get)
|
|
83
|
+
|
|
84
|
+
// List command
|
|
85
|
+
program
|
|
86
|
+
.command('list')
|
|
87
|
+
.description('List your snippets')
|
|
88
|
+
.option('-l, --language <language>', 'Filter by language')
|
|
89
|
+
.option('-n, --limit <number>', 'Limit results', '10')
|
|
90
|
+
.option('--public', 'Show only public snippets')
|
|
91
|
+
.option('--json', 'Output as JSON')
|
|
92
|
+
.action(list)
|
|
93
|
+
|
|
94
|
+
// Search command
|
|
95
|
+
program
|
|
96
|
+
.command('search <query>')
|
|
97
|
+
.description('Search snippets using full-text search')
|
|
98
|
+
.option('-l, --language <language>', 'Filter by language')
|
|
99
|
+
.option('-n, --limit <number>', 'Limit results', '10')
|
|
100
|
+
.option('--mine', 'Search only my snippets')
|
|
101
|
+
.option('--public', 'Search only public snippets')
|
|
102
|
+
.action(search)
|
|
103
|
+
|
|
104
|
+
// Whoami command (check login status)
|
|
105
|
+
program
|
|
106
|
+
.command('whoami')
|
|
107
|
+
.description('Show current logged in user')
|
|
108
|
+
.action(() => {
|
|
109
|
+
const config = getConfig()
|
|
110
|
+
if (config.email) {
|
|
111
|
+
console.log(chalk.green(`✓ Logged in as: ${chalk.bold(config.email)}`))
|
|
112
|
+
} else {
|
|
113
|
+
console.log(chalk.yellow('Not logged in. Run: lumbung login'))
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
// Parse and execute
|
|
118
|
+
program.parse()
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lumbung-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI tool for Lumbung Kode - Gudang Snippet Kode Modern",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"lumbung": "./bin/lumbung.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node bin/lumbung.js",
|
|
12
|
+
"link": "npm link"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"cli",
|
|
16
|
+
"snippet",
|
|
17
|
+
"code",
|
|
18
|
+
"manager",
|
|
19
|
+
"lumbung",
|
|
20
|
+
"lumbung-kode",
|
|
21
|
+
"snippet-manager"
|
|
22
|
+
],
|
|
23
|
+
"author": "Sofyan (sofyan2108)",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/sofyan2108/lumbung-kode-ta.git"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/sofyan2108/lumbung-kode-ta#readme",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@supabase/supabase-js": "^2.86.0",
|
|
32
|
+
"chalk": "^5.3.0",
|
|
33
|
+
"commander": "^12.1.0",
|
|
34
|
+
"conf": "^13.0.1",
|
|
35
|
+
"inquirer": "^9.3.7",
|
|
36
|
+
"ora": "^8.1.1"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Commands - Login & Logout
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import inquirer from 'inquirer'
|
|
6
|
+
import chalk from 'chalk'
|
|
7
|
+
import ora from 'ora'
|
|
8
|
+
import { loginWithEmail } from '../lib/api.js'
|
|
9
|
+
import { saveCredentials, clearCredentials, isLoggedIn, getConfig } from '../lib/config.js'
|
|
10
|
+
|
|
11
|
+
export async function login() {
|
|
12
|
+
// Check if already logged in
|
|
13
|
+
if (isLoggedIn()) {
|
|
14
|
+
const { email } = getConfig()
|
|
15
|
+
console.log(chalk.yellow(`Already logged in as ${chalk.bold(email)}`))
|
|
16
|
+
|
|
17
|
+
const { confirm } = await inquirer.prompt([{
|
|
18
|
+
type: 'confirm',
|
|
19
|
+
name: 'confirm',
|
|
20
|
+
message: 'Do you want to login with a different account?',
|
|
21
|
+
default: false
|
|
22
|
+
}])
|
|
23
|
+
|
|
24
|
+
if (!confirm) return
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
console.log(chalk.cyan('\n📧 Login to Lumbung Kode\n'))
|
|
28
|
+
|
|
29
|
+
// Prompt for credentials
|
|
30
|
+
const answers = await inquirer.prompt([
|
|
31
|
+
{
|
|
32
|
+
type: 'input',
|
|
33
|
+
name: 'email',
|
|
34
|
+
message: 'Email:',
|
|
35
|
+
validate: (input) => {
|
|
36
|
+
if (!input.includes('@')) return 'Please enter a valid email'
|
|
37
|
+
return true
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
type: 'password',
|
|
42
|
+
name: 'password',
|
|
43
|
+
message: 'Password:',
|
|
44
|
+
mask: '*',
|
|
45
|
+
validate: (input) => {
|
|
46
|
+
if (input.length < 6) return 'Password must be at least 6 characters'
|
|
47
|
+
return true
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
])
|
|
51
|
+
|
|
52
|
+
const spinner = ora('Logging in...').start()
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const { accessToken, refreshToken, user } = await loginWithEmail(
|
|
56
|
+
answers.email,
|
|
57
|
+
answers.password
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
// Save credentials locally
|
|
61
|
+
saveCredentials(accessToken, refreshToken, user.email, user.id)
|
|
62
|
+
|
|
63
|
+
spinner.succeed(chalk.green(`Logged in as ${chalk.bold(user.email)}`))
|
|
64
|
+
console.log(chalk.gray(`\nUser ID: ${user.id}`))
|
|
65
|
+
console.log(chalk.gray('Credentials saved to local config.\n'))
|
|
66
|
+
|
|
67
|
+
} catch (error) {
|
|
68
|
+
spinner.fail(chalk.red('Login failed'))
|
|
69
|
+
console.log(chalk.red(`Error: ${error.message}`))
|
|
70
|
+
console.log(chalk.gray('\nMake sure your email and password are correct.'))
|
|
71
|
+
console.log(chalk.gray('If you don\'t have an account, register at https://Lumbung Kode.app'))
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function logout() {
|
|
76
|
+
if (!isLoggedIn()) {
|
|
77
|
+
console.log(chalk.yellow('Not logged in.'))
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const { email } = getConfig()
|
|
82
|
+
|
|
83
|
+
const { confirm } = await inquirer.prompt([{
|
|
84
|
+
type: 'confirm',
|
|
85
|
+
name: 'confirm',
|
|
86
|
+
message: `Logout from ${email}?`,
|
|
87
|
+
default: true
|
|
88
|
+
}])
|
|
89
|
+
|
|
90
|
+
if (confirm) {
|
|
91
|
+
clearCredentials()
|
|
92
|
+
console.log(chalk.green('✓ Logged out successfully.'))
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get Command - Fetch snippet by ID
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from 'fs'
|
|
6
|
+
import chalk from 'chalk'
|
|
7
|
+
import ora from 'ora'
|
|
8
|
+
import { fetchSnippetById } from '../lib/api.js'
|
|
9
|
+
|
|
10
|
+
export async function get(id, options) {
|
|
11
|
+
const spinner = ora('Fetching snippet...').start()
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const snippet = await fetchSnippetById(id)
|
|
15
|
+
|
|
16
|
+
if (!snippet) {
|
|
17
|
+
spinner.fail(chalk.red('Snippet not found'))
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
spinner.succeed(chalk.green('Snippet fetched!'))
|
|
22
|
+
|
|
23
|
+
// Save to file if --output specified
|
|
24
|
+
if (options.output) {
|
|
25
|
+
fs.writeFileSync(options.output, snippet.code)
|
|
26
|
+
console.log(chalk.green(`\n✓ Saved to: ${options.output}`))
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Copy to clipboard if --copy specified
|
|
31
|
+
if (options.copy) {
|
|
32
|
+
try {
|
|
33
|
+
const { execSync } = await import('child_process')
|
|
34
|
+
|
|
35
|
+
// Cross-platform clipboard
|
|
36
|
+
if (process.platform === 'win32') {
|
|
37
|
+
execSync(`echo ${snippet.code.replace(/"/g, '\\"')} | clip`, { stdio: 'ignore' })
|
|
38
|
+
} else if (process.platform === 'darwin') {
|
|
39
|
+
execSync(`echo "${snippet.code}" | pbcopy`, { stdio: 'ignore' })
|
|
40
|
+
} else {
|
|
41
|
+
execSync(`echo "${snippet.code}" | xclip -selection clipboard`, { stdio: 'ignore' })
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.log(chalk.green('\n✓ Copied to clipboard!'))
|
|
45
|
+
} catch (e) {
|
|
46
|
+
console.log(chalk.yellow('\n⚠ Could not copy to clipboard'))
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Display snippet info and code
|
|
51
|
+
console.log('')
|
|
52
|
+
console.log(chalk.gray('═'.repeat(60)))
|
|
53
|
+
console.log(chalk.bold.cyan(`📝 ${snippet.title}`))
|
|
54
|
+
console.log(chalk.gray('═'.repeat(60)))
|
|
55
|
+
console.log(`${chalk.gray('Language:')} ${chalk.yellow(snippet.language)}`)
|
|
56
|
+
console.log(`${chalk.gray('Public:')} ${snippet.is_public ? '🌍 Yes' : '🔒 No'}`)
|
|
57
|
+
if (snippet.description) {
|
|
58
|
+
console.log(`${chalk.gray('Desc:')} ${snippet.description}`)
|
|
59
|
+
}
|
|
60
|
+
if (snippet.tags && snippet.tags.length > 0) {
|
|
61
|
+
console.log(`${chalk.gray('Tags:')} ${snippet.tags.map(t => chalk.blue(`#${t}`)).join(' ')}`)
|
|
62
|
+
}
|
|
63
|
+
console.log(chalk.gray('─'.repeat(60)))
|
|
64
|
+
console.log('')
|
|
65
|
+
console.log(snippet.code)
|
|
66
|
+
console.log('')
|
|
67
|
+
console.log(chalk.gray('─'.repeat(60)))
|
|
68
|
+
console.log(chalk.gray(`Stats: ❤️ ${snippet.like_count || 0} likes | 📋 ${snippet.copy_count || 0} copies`))
|
|
69
|
+
|
|
70
|
+
} catch (error) {
|
|
71
|
+
spinner.fail(chalk.red('Failed to fetch snippet'))
|
|
72
|
+
console.log(chalk.red(`Error: ${error.message}`))
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* List Command - List user snippets
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import chalk from 'chalk'
|
|
6
|
+
import ora from 'ora'
|
|
7
|
+
import { fetchMySnippets } from '../lib/api.js'
|
|
8
|
+
import { isLoggedIn } from '../lib/config.js'
|
|
9
|
+
|
|
10
|
+
export async function list(options) {
|
|
11
|
+
// Check login
|
|
12
|
+
if (!isLoggedIn()) {
|
|
13
|
+
console.log(chalk.red('✗ Not logged in. Run: codehaven login'))
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const spinner = ora('Fetching your snippets...').start()
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const snippets = await fetchMySnippets({
|
|
21
|
+
language: options.language,
|
|
22
|
+
limit: options.limit,
|
|
23
|
+
isPublic: options.public ? true : undefined
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
spinner.stop()
|
|
27
|
+
|
|
28
|
+
if (snippets.length === 0) {
|
|
29
|
+
console.log(chalk.yellow('\n📭 No snippets found.'))
|
|
30
|
+
console.log(chalk.gray('Create your first snippet: codehaven push <file>'))
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// JSON output
|
|
35
|
+
if (options.json) {
|
|
36
|
+
console.log(JSON.stringify(snippets, null, 2))
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Table output
|
|
41
|
+
console.log('')
|
|
42
|
+
console.log(chalk.bold.cyan(`📚 Your Snippets (${snippets.length})`))
|
|
43
|
+
console.log(chalk.gray('─'.repeat(80)))
|
|
44
|
+
console.log(
|
|
45
|
+
chalk.gray('ID'.padEnd(38)) +
|
|
46
|
+
chalk.gray('Title'.padEnd(25)) +
|
|
47
|
+
chalk.gray('Lang'.padEnd(12)) +
|
|
48
|
+
chalk.gray('Vis')
|
|
49
|
+
)
|
|
50
|
+
console.log(chalk.gray('─'.repeat(80)))
|
|
51
|
+
|
|
52
|
+
snippets.forEach(s => {
|
|
53
|
+
const visibility = s.is_public ? chalk.green('🌍') : chalk.gray('🔒')
|
|
54
|
+
const truncatedTitle = s.title.length > 22 ? s.title.slice(0, 22) + '...' : s.title
|
|
55
|
+
|
|
56
|
+
console.log(
|
|
57
|
+
chalk.gray(s.id.padEnd(38)) +
|
|
58
|
+
chalk.white(truncatedTitle.padEnd(25)) +
|
|
59
|
+
chalk.yellow(s.language.padEnd(12)) +
|
|
60
|
+
visibility
|
|
61
|
+
)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
console.log(chalk.gray('─'.repeat(80)))
|
|
65
|
+
console.log(chalk.gray(`\nShowing ${snippets.length} snippets. Use --limit to see more.`))
|
|
66
|
+
console.log(chalk.gray('View snippet: codehaven get <id>'))
|
|
67
|
+
|
|
68
|
+
} catch (error) {
|
|
69
|
+
spinner.fail(chalk.red('Failed to fetch snippets'))
|
|
70
|
+
console.log(chalk.red(`Error: ${error.message}`))
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Push Command - Upload snippet from file
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from 'fs'
|
|
6
|
+
import path from 'path'
|
|
7
|
+
import chalk from 'chalk'
|
|
8
|
+
import ora from 'ora'
|
|
9
|
+
import { createSnippet } from '../lib/api.js'
|
|
10
|
+
import { isLoggedIn } from '../lib/config.js'
|
|
11
|
+
|
|
12
|
+
// Extension to language mapping
|
|
13
|
+
const EXTENSION_MAP = {
|
|
14
|
+
'.js': 'javascript',
|
|
15
|
+
'.jsx': 'javascript',
|
|
16
|
+
'.ts': 'typescript',
|
|
17
|
+
'.tsx': 'typescript',
|
|
18
|
+
'.py': 'python',
|
|
19
|
+
'.java': 'java',
|
|
20
|
+
'.cpp': 'cpp',
|
|
21
|
+
'.c': 'c',
|
|
22
|
+
'.cs': 'csharp',
|
|
23
|
+
'.go': 'go',
|
|
24
|
+
'.rs': 'rust',
|
|
25
|
+
'.rb': 'ruby',
|
|
26
|
+
'.php': 'php',
|
|
27
|
+
'.html': 'html',
|
|
28
|
+
'.css': 'css',
|
|
29
|
+
'.scss': 'scss',
|
|
30
|
+
'.sql': 'sql',
|
|
31
|
+
'.md': 'markdown',
|
|
32
|
+
'.json': 'json',
|
|
33
|
+
'.yaml': 'yaml',
|
|
34
|
+
'.yml': 'yaml',
|
|
35
|
+
'.xml': 'xml',
|
|
36
|
+
'.sh': 'bash',
|
|
37
|
+
'.bat': 'batch',
|
|
38
|
+
'.ps1': 'powershell'
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function push(file, options) {
|
|
42
|
+
// Check login
|
|
43
|
+
if (!isLoggedIn()) {
|
|
44
|
+
console.log(chalk.red('✗ Not logged in. Run: codehaven login'))
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Check file exists
|
|
49
|
+
if (!fs.existsSync(file)) {
|
|
50
|
+
console.log(chalk.red(`✗ File not found: ${file}`))
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const spinner = ora('Reading file...').start()
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
// Read file content
|
|
58
|
+
const code = fs.readFileSync(file, 'utf-8')
|
|
59
|
+
|
|
60
|
+
if (!code.trim()) {
|
|
61
|
+
spinner.fail(chalk.red('File is empty'))
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Detect language from extension
|
|
66
|
+
const ext = path.extname(file).toLowerCase()
|
|
67
|
+
const detectedLanguage = EXTENSION_MAP[ext] || 'plaintext'
|
|
68
|
+
|
|
69
|
+
// Use provided options or defaults
|
|
70
|
+
const title = options.title || path.basename(file, ext)
|
|
71
|
+
const language = options.language || detectedLanguage
|
|
72
|
+
const description = options.description || ''
|
|
73
|
+
const tags = options.tags ? options.tags.split(',').map(t => t.trim()) : []
|
|
74
|
+
const isPublic = options.public || false
|
|
75
|
+
|
|
76
|
+
spinner.text = 'Uploading snippet...'
|
|
77
|
+
|
|
78
|
+
// Create snippet
|
|
79
|
+
const snippet = await createSnippet({
|
|
80
|
+
title,
|
|
81
|
+
language,
|
|
82
|
+
code,
|
|
83
|
+
description,
|
|
84
|
+
tags,
|
|
85
|
+
is_public: isPublic
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
spinner.succeed(chalk.green('Snippet uploaded successfully!'))
|
|
89
|
+
|
|
90
|
+
// Display result
|
|
91
|
+
console.log('')
|
|
92
|
+
console.log(chalk.gray('─'.repeat(50)))
|
|
93
|
+
console.log(chalk.bold('📝 Snippet Details:'))
|
|
94
|
+
console.log(chalk.gray('─'.repeat(50)))
|
|
95
|
+
console.log(` ${chalk.cyan('ID:')} ${snippet.id}`)
|
|
96
|
+
console.log(` ${chalk.cyan('Title:')} ${snippet.title}`)
|
|
97
|
+
console.log(` ${chalk.cyan('Language:')} ${snippet.language}`)
|
|
98
|
+
console.log(` ${chalk.cyan('Public:')} ${snippet.is_public ? '🌍 Yes' : '🔒 No'}`)
|
|
99
|
+
if (tags.length > 0) {
|
|
100
|
+
console.log(` ${chalk.cyan('Tags:')} ${tags.map(t => `#${t}`).join(' ')}`)
|
|
101
|
+
}
|
|
102
|
+
console.log(chalk.gray('─'.repeat(50)))
|
|
103
|
+
console.log('')
|
|
104
|
+
console.log(chalk.gray(`View at: https://codehaven.app/snippet/${snippet.id}`))
|
|
105
|
+
|
|
106
|
+
} catch (error) {
|
|
107
|
+
spinner.fail(chalk.red('Failed to upload snippet'))
|
|
108
|
+
console.log(chalk.red(`Error: ${error.message}`))
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search Command - Full-text search snippets
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import chalk from 'chalk'
|
|
6
|
+
import ora from 'ora'
|
|
7
|
+
import { searchSnippets } from '../lib/api.js'
|
|
8
|
+
|
|
9
|
+
export async function search(query, options) {
|
|
10
|
+
if (query.length < 3) {
|
|
11
|
+
console.log(chalk.yellow('⚠ Search query must be at least 3 characters'))
|
|
12
|
+
return
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const spinner = ora(`Searching for "${query}"...`).start()
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const snippets = await searchSnippets(query, {
|
|
19
|
+
language: options.language,
|
|
20
|
+
limit: options.limit,
|
|
21
|
+
mine: options.mine,
|
|
22
|
+
publicOnly: options.public
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
spinner.stop()
|
|
26
|
+
|
|
27
|
+
if (snippets.length === 0) {
|
|
28
|
+
console.log(chalk.yellow(`\n🔍 No results for "${query}"`))
|
|
29
|
+
console.log(chalk.gray('Try different keywords or check spelling.'))
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Display results
|
|
34
|
+
console.log('')
|
|
35
|
+
console.log(chalk.bold.cyan(`🔍 Search Results: "${query}" (${snippets.length} found)`))
|
|
36
|
+
console.log(chalk.gray('─'.repeat(80)))
|
|
37
|
+
|
|
38
|
+
snippets.forEach((s, i) => {
|
|
39
|
+
const visibility = s.is_public ? chalk.green('🌍 Public') : chalk.gray('🔒 Private')
|
|
40
|
+
|
|
41
|
+
console.log('')
|
|
42
|
+
console.log(`${chalk.bold.white(`${i + 1}. ${s.title}`)}`)
|
|
43
|
+
console.log(chalk.gray(` ID: ${s.id}`))
|
|
44
|
+
console.log(` ${chalk.yellow(s.language)} | ${visibility} | ❤️ ${s.like_count || 0}`)
|
|
45
|
+
|
|
46
|
+
if (s.description) {
|
|
47
|
+
const desc = s.description.length > 60 ? s.description.slice(0, 60) + '...' : s.description
|
|
48
|
+
console.log(chalk.gray(` ${desc}`))
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (s.tags && s.tags.length > 0) {
|
|
52
|
+
console.log(` ${s.tags.map(t => chalk.blue(`#${t}`)).join(' ')}`)
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
console.log('')
|
|
57
|
+
console.log(chalk.gray('─'.repeat(80)))
|
|
58
|
+
console.log(chalk.gray('Fetch snippet: codehaven get <id>'))
|
|
59
|
+
|
|
60
|
+
} catch (error) {
|
|
61
|
+
spinner.fail(chalk.red('Search failed'))
|
|
62
|
+
console.log(chalk.red(`Error: ${error.message}`))
|
|
63
|
+
}
|
|
64
|
+
}
|
package/src/lib/api.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supabase API Client for CLI
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { createClient } from '@supabase/supabase-js'
|
|
6
|
+
import { getConfig } from './config.js'
|
|
7
|
+
|
|
8
|
+
// Supabase credentials (same as web app)
|
|
9
|
+
const SUPABASE_URL = 'https://pjzfmcsypilbhmzcxlbt.supabase.co'
|
|
10
|
+
const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBqemZtY3N5cGlsYmhtemN4bGJ0Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzQwNTI0NjksImV4cCI6MjA0OTYyODQ2OX0.nE-Oi9xYm6NlI6R4MokXJcG75Wf4P_lEIaEiQzpPfGk'
|
|
11
|
+
|
|
12
|
+
// Create Supabase client
|
|
13
|
+
export function createSupabaseClient() {
|
|
14
|
+
const { accessToken } = getConfig()
|
|
15
|
+
|
|
16
|
+
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
|
|
17
|
+
auth: {
|
|
18
|
+
autoRefreshToken: false,
|
|
19
|
+
persistSession: false
|
|
20
|
+
},
|
|
21
|
+
global: {
|
|
22
|
+
headers: accessToken ? {
|
|
23
|
+
Authorization: `Bearer ${accessToken}`
|
|
24
|
+
} : {}
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
return supabase
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Login with email/password
|
|
32
|
+
export async function loginWithEmail(email, password) {
|
|
33
|
+
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY)
|
|
34
|
+
|
|
35
|
+
const { data, error } = await supabase.auth.signInWithPassword({
|
|
36
|
+
email,
|
|
37
|
+
password
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
if (error) throw error
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
accessToken: data.session.access_token,
|
|
44
|
+
refreshToken: data.session.refresh_token,
|
|
45
|
+
user: data.user
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Fetch user snippets
|
|
50
|
+
export async function fetchMySnippets(options = {}) {
|
|
51
|
+
const supabase = createSupabaseClient()
|
|
52
|
+
const { userId } = getConfig()
|
|
53
|
+
|
|
54
|
+
let query = supabase
|
|
55
|
+
.from('snippets')
|
|
56
|
+
.select('id, title, language, description, tags, is_public, created_at, like_count, copy_count')
|
|
57
|
+
.eq('user_id', userId)
|
|
58
|
+
.order('created_at', { ascending: false })
|
|
59
|
+
|
|
60
|
+
if (options.language) {
|
|
61
|
+
query = query.ilike('language', options.language)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (options.isPublic !== undefined) {
|
|
65
|
+
query = query.eq('is_public', options.isPublic)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (options.limit) {
|
|
69
|
+
query = query.limit(parseInt(options.limit))
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const { data, error } = await query
|
|
73
|
+
|
|
74
|
+
if (error) throw error
|
|
75
|
+
return data
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Fetch snippet by ID
|
|
79
|
+
export async function fetchSnippetById(id) {
|
|
80
|
+
const supabase = createSupabaseClient()
|
|
81
|
+
|
|
82
|
+
const { data, error } = await supabase
|
|
83
|
+
.from('snippets')
|
|
84
|
+
.select('*')
|
|
85
|
+
.eq('id', id)
|
|
86
|
+
.single()
|
|
87
|
+
|
|
88
|
+
if (error) throw error
|
|
89
|
+
return data
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Create new snippet
|
|
93
|
+
export async function createSnippet(snippetData) {
|
|
94
|
+
const supabase = createSupabaseClient()
|
|
95
|
+
const { userId } = getConfig()
|
|
96
|
+
|
|
97
|
+
const { data, error } = await supabase
|
|
98
|
+
.from('snippets')
|
|
99
|
+
.insert({
|
|
100
|
+
...snippetData,
|
|
101
|
+
user_id: userId
|
|
102
|
+
})
|
|
103
|
+
.select()
|
|
104
|
+
.single()
|
|
105
|
+
|
|
106
|
+
if (error) throw error
|
|
107
|
+
return data
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Search snippets with full-text search
|
|
111
|
+
export async function searchSnippets(query, options = {}) {
|
|
112
|
+
const supabase = createSupabaseClient()
|
|
113
|
+
const { userId } = getConfig()
|
|
114
|
+
|
|
115
|
+
let dbQuery = supabase
|
|
116
|
+
.from('snippets')
|
|
117
|
+
.select('id, title, language, description, tags, is_public, created_at, like_count')
|
|
118
|
+
.textSearch('search_vector', query)
|
|
119
|
+
.order('created_at', { ascending: false })
|
|
120
|
+
|
|
121
|
+
// Filter by ownership
|
|
122
|
+
if (options.mine) {
|
|
123
|
+
dbQuery = dbQuery.eq('user_id', userId)
|
|
124
|
+
} else if (options.publicOnly) {
|
|
125
|
+
dbQuery = dbQuery.eq('is_public', true)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Filter by language
|
|
129
|
+
if (options.language) {
|
|
130
|
+
dbQuery = dbQuery.ilike('language', options.language)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Limit results
|
|
134
|
+
if (options.limit) {
|
|
135
|
+
dbQuery = dbQuery.limit(parseInt(options.limit))
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const { data, error } = await dbQuery
|
|
139
|
+
|
|
140
|
+
if (error) throw error
|
|
141
|
+
return data
|
|
142
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config Storage - Stores API credentials locally
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import Conf from 'conf'
|
|
6
|
+
|
|
7
|
+
const config = new Conf({
|
|
8
|
+
projectName: 'codehaven-cli',
|
|
9
|
+
schema: {
|
|
10
|
+
accessToken: { type: 'string', default: '' },
|
|
11
|
+
refreshToken: { type: 'string', default: '' },
|
|
12
|
+
email: { type: 'string', default: '' },
|
|
13
|
+
userId: { type: 'string', default: '' }
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
export function saveCredentials(accessToken, refreshToken, email, userId) {
|
|
18
|
+
config.set('accessToken', accessToken)
|
|
19
|
+
config.set('refreshToken', refreshToken)
|
|
20
|
+
config.set('email', email)
|
|
21
|
+
config.set('userId', userId)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function getConfig() {
|
|
25
|
+
return {
|
|
26
|
+
accessToken: config.get('accessToken'),
|
|
27
|
+
refreshToken: config.get('refreshToken'),
|
|
28
|
+
email: config.get('email'),
|
|
29
|
+
userId: config.get('userId')
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function clearCredentials() {
|
|
34
|
+
config.clear()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function isLoggedIn() {
|
|
38
|
+
return !!config.get('accessToken')
|
|
39
|
+
}
|