@micropage-sh/cli 2.0.0 → 2.0.2
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/package.json +1 -1
- package/src/commands/login.js +38 -7
- package/src/commands/logout.js +10 -2
- package/src/commands/whoami.js +24 -17
- package/src/index.js +1 -1
- package/src/plan.js +40 -0
package/package.json
CHANGED
package/src/commands/login.js
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
const http = require('http');
|
|
4
4
|
const { randomBytes } = require('crypto');
|
|
5
|
-
const { setSession, getSession } = require('../auth');
|
|
5
|
+
const { setSession, getSession, clearSession } = require('../auth');
|
|
6
6
|
const { getUserInfo, getValidAccessToken } = require('../supabase');
|
|
7
7
|
const { APP_URL, SUPABASE_URL, SUPABASE_ANON_KEY } = require('../config');
|
|
8
|
+
const { UPGRADE_MESSAGE, isPaidTier, getPlanTierWithToken } = require('../plan');
|
|
8
9
|
|
|
9
10
|
// Find an available ephemeral port by briefly binding to port 0.
|
|
10
11
|
function getFreePort() {
|
|
@@ -164,11 +165,24 @@ async function run(options = {}) {
|
|
|
164
165
|
const existing = getSession();
|
|
165
166
|
if (existing) {
|
|
166
167
|
try {
|
|
167
|
-
await getValidAccessToken();
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
168
|
+
const accessToken = await getValidAccessToken();
|
|
169
|
+
const planTier = await getPlanTierWithToken(existing.user?.id, accessToken);
|
|
170
|
+
if (!isPaidTier(planTier)) {
|
|
171
|
+
clearSession();
|
|
172
|
+
console.error(UPGRADE_MESSAGE);
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
const displayName = existing.user?.email
|
|
176
|
+
|| existing.user?.user_metadata?.full_name
|
|
177
|
+
|| existing.user?.user_metadata?.user_name
|
|
178
|
+
|| existing.user?.user_metadata?.name
|
|
179
|
+
|| existing.user?.id;
|
|
180
|
+
if (displayName) {
|
|
181
|
+
console.log(`Already logged in as ${displayName}.`);
|
|
182
|
+
console.log('Use --force to re-authenticate.');
|
|
183
|
+
process.exit(0);
|
|
184
|
+
}
|
|
185
|
+
// No identifiable display name on the cached user — fall through to a fresh login.
|
|
172
186
|
} catch {
|
|
173
187
|
// Token expired or refresh failed — fall through to re-login
|
|
174
188
|
}
|
|
@@ -240,10 +254,27 @@ async function run(options = {}) {
|
|
|
240
254
|
server.close();
|
|
241
255
|
|
|
242
256
|
const user = await getUserInfo(access_token);
|
|
257
|
+
if (!user?.id) {
|
|
258
|
+
console.error('Login failed: could not load user profile.');
|
|
259
|
+
process.exit(1);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const planTier = await getPlanTierWithToken(user.id, access_token);
|
|
263
|
+
if (!isPaidTier(planTier)) {
|
|
264
|
+
// Free-tier (or unconfirmed) users get no session at all.
|
|
265
|
+
console.error(UPGRADE_MESSAGE);
|
|
266
|
+
process.exit(1);
|
|
267
|
+
}
|
|
268
|
+
|
|
243
269
|
setSession({ access_token, refresh_token, user });
|
|
244
270
|
|
|
245
|
-
const displayName = user
|
|
271
|
+
const displayName = user.email
|
|
272
|
+
|| user.user_metadata?.full_name
|
|
273
|
+
|| user.user_metadata?.user_name
|
|
274
|
+
|| user.user_metadata?.name
|
|
275
|
+
|| user.id;
|
|
246
276
|
console.log(`Logged in as ${displayName}`);
|
|
277
|
+
process.exit(0);
|
|
247
278
|
} catch (err) {
|
|
248
279
|
clearTimeout(timeout);
|
|
249
280
|
server.close();
|
package/src/commands/logout.js
CHANGED
|
@@ -9,8 +9,16 @@ function run() {
|
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
11
11
|
clearSession();
|
|
12
|
-
const
|
|
13
|
-
|
|
12
|
+
const displayName = session.user?.email
|
|
13
|
+
|| session.user?.user_metadata?.full_name
|
|
14
|
+
|| session.user?.user_metadata?.user_name
|
|
15
|
+
|| session.user?.user_metadata?.name
|
|
16
|
+
|| session.user?.id;
|
|
17
|
+
if (displayName) {
|
|
18
|
+
console.log(`Logged out (was: ${displayName}).`);
|
|
19
|
+
} else {
|
|
20
|
+
console.log('Logged out.');
|
|
21
|
+
}
|
|
14
22
|
}
|
|
15
23
|
|
|
16
24
|
module.exports = { run };
|
package/src/commands/whoami.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { getSession } = require('../auth');
|
|
3
|
+
const { getSession, clearSession } = require('../auth');
|
|
4
4
|
const { db, getUserInfo, getValidAccessToken, handleAuthError } = require('../supabase');
|
|
5
|
+
const { UPGRADE_MESSAGE, isPaidTier } = require('../plan');
|
|
5
6
|
|
|
6
7
|
async function run() {
|
|
7
8
|
const session = getSession();
|
|
@@ -25,35 +26,41 @@ async function run() {
|
|
|
25
26
|
process.exit(1);
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
console.log('Email: ', user.email || '-');
|
|
30
|
-
const name =
|
|
31
|
-
user.user_metadata?.full_name ||
|
|
32
|
-
user.user_metadata?.user_name ||
|
|
33
|
-
user.user_metadata?.name ||
|
|
34
|
-
'-';
|
|
35
|
-
console.log('Name: ', name);
|
|
36
|
-
if (user.app_metadata?.provider) {
|
|
37
|
-
console.log('Auth via: ', user.app_metadata.provider);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Billing / subscription overview (best-effort; do not fail whoami if this breaks).
|
|
29
|
+
// Gate access on plan first — free-tier accounts get no session and no detail dump.
|
|
41
30
|
let planTier = 'free';
|
|
42
|
-
let subscription = null;
|
|
43
31
|
try {
|
|
44
32
|
const customer = await db
|
|
45
33
|
.from('customers')
|
|
46
34
|
.select('plan_tier')
|
|
47
35
|
.eq('user_id', user.id)
|
|
48
36
|
.single();
|
|
49
|
-
if (customer?.plan_tier) {
|
|
37
|
+
if (customer?.plan_tier === 'pro' || customer?.plan_tier === 'pro_plus') {
|
|
50
38
|
planTier = customer.plan_tier;
|
|
51
39
|
}
|
|
52
40
|
} catch (err) {
|
|
53
41
|
handleAuthError(err);
|
|
54
|
-
|
|
42
|
+
// If we cannot determine plan, default to free → deny CLI access below.
|
|
55
43
|
}
|
|
56
44
|
|
|
45
|
+
if (!isPaidTier(planTier)) {
|
|
46
|
+
clearSession();
|
|
47
|
+
console.error(UPGRADE_MESSAGE);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
console.log('User ID: ', user.id);
|
|
52
|
+
console.log('Email: ', user.email || '-');
|
|
53
|
+
const name =
|
|
54
|
+
user.user_metadata?.full_name ||
|
|
55
|
+
user.user_metadata?.user_name ||
|
|
56
|
+
user.user_metadata?.name ||
|
|
57
|
+
'-';
|
|
58
|
+
console.log('Name: ', name);
|
|
59
|
+
if (user.app_metadata?.provider) {
|
|
60
|
+
console.log('Auth via: ', user.app_metadata.provider);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let subscription = null;
|
|
57
64
|
try {
|
|
58
65
|
const subs = await db
|
|
59
66
|
.from('subscriptions')
|
package/src/index.js
CHANGED
|
@@ -18,7 +18,7 @@ const program = new Command();
|
|
|
18
18
|
program
|
|
19
19
|
.name('micropage')
|
|
20
20
|
.description('CLI for micropage.sh – create, sync, and publish microsites')
|
|
21
|
-
.version('2.0.
|
|
21
|
+
.version('2.0.2');
|
|
22
22
|
|
|
23
23
|
// ---------------------------------------------------------------------------
|
|
24
24
|
// Authentication
|
package/src/plan.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { SUPABASE_URL, SUPABASE_ANON_KEY } = require('./config');
|
|
4
|
+
|
|
5
|
+
const UPGRADE_MESSAGE = [
|
|
6
|
+
'The micropage CLI is available on paid plans only.',
|
|
7
|
+
'Your account is currently on the free plan.',
|
|
8
|
+
'',
|
|
9
|
+
'Upgrade at: https://micropage.sh/pricing',
|
|
10
|
+
].join('\n');
|
|
11
|
+
|
|
12
|
+
function isPaidTier(tier) {
|
|
13
|
+
return tier === 'pro' || tier === 'pro_plus';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Look up plan_tier using a bearer token directly so this can be called inside
|
|
17
|
+
// `login` before a session is persisted. Defaults to 'free' on any failure —
|
|
18
|
+
// CLI access is denied when we cannot confirm a paid tier.
|
|
19
|
+
async function getPlanTierWithToken(userId, accessToken) {
|
|
20
|
+
try {
|
|
21
|
+
const url = `${SUPABASE_URL}/rest/v1/customers?select=plan_tier&user_id=eq.${encodeURIComponent(userId)}&limit=1`;
|
|
22
|
+
const res = await fetch(url, {
|
|
23
|
+
headers: {
|
|
24
|
+
apikey: SUPABASE_ANON_KEY,
|
|
25
|
+
Authorization: `Bearer ${accessToken}`,
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
if (!res.ok) return 'free';
|
|
29
|
+
const data = await res.json();
|
|
30
|
+
const row = Array.isArray(data) ? data[0] : null;
|
|
31
|
+
if (row?.plan_tier === 'pro' || row?.plan_tier === 'pro_plus') {
|
|
32
|
+
return row.plan_tier;
|
|
33
|
+
}
|
|
34
|
+
return 'free';
|
|
35
|
+
} catch {
|
|
36
|
+
return 'free';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = { UPGRADE_MESSAGE, isPaidTier, getPlanTierWithToken };
|