@geotechcli/core 0.2.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/dist/agents/brain.d.ts +39 -0
- package/dist/agents/brain.d.ts.map +1 -0
- package/dist/agents/brain.js +339 -0
- package/dist/agents/brain.js.map +1 -0
- package/dist/agents/bridge-tools.d.ts +2 -0
- package/dist/agents/bridge-tools.d.ts.map +1 -0
- package/dist/agents/bridge-tools.js +170 -0
- package/dist/agents/bridge-tools.js.map +1 -0
- package/dist/agents/data-tools.d.ts +2 -0
- package/dist/agents/data-tools.d.ts.map +1 -0
- package/dist/agents/data-tools.js +309 -0
- package/dist/agents/data-tools.js.map +1 -0
- package/dist/agents/filesystem-tools.d.ts +2 -0
- package/dist/agents/filesystem-tools.d.ts.map +1 -0
- package/dist/agents/filesystem-tools.js +267 -0
- package/dist/agents/filesystem-tools.js.map +1 -0
- package/dist/agents/guardrails.d.ts +17 -0
- package/dist/agents/guardrails.d.ts.map +1 -0
- package/dist/agents/guardrails.js +260 -0
- package/dist/agents/guardrails.js.map +1 -0
- package/dist/agents/orchestrator.d.ts +9 -0
- package/dist/agents/orchestrator.d.ts.map +1 -0
- package/dist/agents/orchestrator.js +136 -0
- package/dist/agents/orchestrator.js.map +1 -0
- package/dist/agents/safety.d.ts +9 -0
- package/dist/agents/safety.d.ts.map +1 -0
- package/dist/agents/safety.js +40 -0
- package/dist/agents/safety.js.map +1 -0
- package/dist/agents/sandbox.d.ts +34 -0
- package/dist/agents/sandbox.d.ts.map +1 -0
- package/dist/agents/sandbox.js +235 -0
- package/dist/agents/sandbox.js.map +1 -0
- package/dist/agents/swarm.d.ts +25 -0
- package/dist/agents/swarm.d.ts.map +1 -0
- package/dist/agents/swarm.js +434 -0
- package/dist/agents/swarm.js.map +1 -0
- package/dist/agents/tools.d.ts +37 -0
- package/dist/agents/tools.d.ts.map +1 -0
- package/dist/agents/tools.js +451 -0
- package/dist/agents/tools.js.map +1 -0
- package/dist/bridge/index.d.ts +52 -0
- package/dist/bridge/index.d.ts.map +1 -0
- package/dist/bridge/index.js +195 -0
- package/dist/bridge/index.js.map +1 -0
- package/dist/config/index.d.ts +106 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +217 -0
- package/dist/config/index.js.map +1 -0
- package/dist/db/index.d.ts +4 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +4 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/redis.d.ts +14 -0
- package/dist/db/redis.d.ts.map +1 -0
- package/dist/db/redis.js +204 -0
- package/dist/db/redis.js.map +1 -0
- package/dist/db/supabase.d.ts +57 -0
- package/dist/db/supabase.d.ts.map +1 -0
- package/dist/db/supabase.js +156 -0
- package/dist/db/supabase.js.map +1 -0
- package/dist/db/users.d.ts +50 -0
- package/dist/db/users.d.ts.map +1 -0
- package/dist/db/users.js +132 -0
- package/dist/db/users.js.map +1 -0
- package/dist/export/index.d.ts +51 -0
- package/dist/export/index.d.ts.map +1 -0
- package/dist/export/index.js +126 -0
- package/dist/export/index.js.map +1 -0
- package/dist/geo/bearing-capacity.d.ts +60 -0
- package/dist/geo/bearing-capacity.d.ts.map +1 -0
- package/dist/geo/bearing-capacity.js +195 -0
- package/dist/geo/bearing-capacity.js.map +1 -0
- package/dist/geo/classification.d.ts +107 -0
- package/dist/geo/classification.d.ts.map +1 -0
- package/dist/geo/classification.js +261 -0
- package/dist/geo/classification.js.map +1 -0
- package/dist/geo/index.d.ts +9 -0
- package/dist/geo/index.d.ts.map +1 -0
- package/dist/geo/index.js +9 -0
- package/dist/geo/index.js.map +1 -0
- package/dist/geo/lateral-earth-pressure.d.ts +75 -0
- package/dist/geo/lateral-earth-pressure.d.ts.map +1 -0
- package/dist/geo/lateral-earth-pressure.js +219 -0
- package/dist/geo/lateral-earth-pressure.js.map +1 -0
- package/dist/geo/liquefaction.d.ts +65 -0
- package/dist/geo/liquefaction.d.ts.map +1 -0
- package/dist/geo/liquefaction.js +163 -0
- package/dist/geo/liquefaction.js.map +1 -0
- package/dist/geo/pile-capacity.d.ts +91 -0
- package/dist/geo/pile-capacity.d.ts.map +1 -0
- package/dist/geo/pile-capacity.js +233 -0
- package/dist/geo/pile-capacity.js.map +1 -0
- package/dist/geo/settlement.d.ts +119 -0
- package/dist/geo/settlement.d.ts.map +1 -0
- package/dist/geo/settlement.js +184 -0
- package/dist/geo/settlement.js.map +1 -0
- package/dist/geo/slope-stability.d.ts +82 -0
- package/dist/geo/slope-stability.d.ts.map +1 -0
- package/dist/geo/slope-stability.js +214 -0
- package/dist/geo/slope-stability.js.map +1 -0
- package/dist/geo/tunnel/index.d.ts +2 -0
- package/dist/geo/tunnel/index.d.ts.map +1 -0
- package/dist/geo/tunnel/index.js +2 -0
- package/dist/geo/tunnel/index.js.map +1 -0
- package/dist/geo/tunnel/tbm.d.ts +135 -0
- package/dist/geo/tunnel/tbm.d.ts.map +1 -0
- package/dist/geo/tunnel/tbm.js +268 -0
- package/dist/geo/tunnel/tbm.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/ingest/ags.d.ts +42 -0
- package/dist/ingest/ags.d.ts.map +1 -0
- package/dist/ingest/ags.js +133 -0
- package/dist/ingest/ags.js.map +1 -0
- package/dist/ingest/cpt.d.ts +47 -0
- package/dist/ingest/cpt.d.ts.map +1 -0
- package/dist/ingest/cpt.js +112 -0
- package/dist/ingest/cpt.js.map +1 -0
- package/dist/ingest/index.d.ts +3 -0
- package/dist/ingest/index.d.ts.map +1 -0
- package/dist/ingest/index.js +3 -0
- package/dist/ingest/index.js.map +1 -0
- package/dist/llm/index.d.ts +5 -0
- package/dist/llm/index.d.ts.map +1 -0
- package/dist/llm/index.js +4 -0
- package/dist/llm/index.js.map +1 -0
- package/dist/llm/middleware/metering.d.ts +55 -0
- package/dist/llm/middleware/metering.d.ts.map +1 -0
- package/dist/llm/middleware/metering.js +191 -0
- package/dist/llm/middleware/metering.js.map +1 -0
- package/dist/llm/middleware/persistent-usage.d.ts +7 -0
- package/dist/llm/middleware/persistent-usage.d.ts.map +1 -0
- package/dist/llm/middleware/persistent-usage.js +108 -0
- package/dist/llm/middleware/persistent-usage.js.map +1 -0
- package/dist/llm/middleware/retry.d.ts +7 -0
- package/dist/llm/middleware/retry.d.ts.map +1 -0
- package/dist/llm/middleware/retry.js +29 -0
- package/dist/llm/middleware/retry.js.map +1 -0
- package/dist/llm/providers/anthropic.d.ts +10 -0
- package/dist/llm/providers/anthropic.d.ts.map +1 -0
- package/dist/llm/providers/anthropic.js +107 -0
- package/dist/llm/providers/anthropic.js.map +1 -0
- package/dist/llm/providers/hosted-beta.d.ts +10 -0
- package/dist/llm/providers/hosted-beta.d.ts.map +1 -0
- package/dist/llm/providers/hosted-beta.js +106 -0
- package/dist/llm/providers/hosted-beta.js.map +1 -0
- package/dist/llm/providers/huggingface.d.ts +37 -0
- package/dist/llm/providers/huggingface.d.ts.map +1 -0
- package/dist/llm/providers/huggingface.js +133 -0
- package/dist/llm/providers/huggingface.js.map +1 -0
- package/dist/llm/providers/openai-compatible.d.ts +27 -0
- package/dist/llm/providers/openai-compatible.d.ts.map +1 -0
- package/dist/llm/providers/openai-compatible.js +99 -0
- package/dist/llm/providers/openai-compatible.js.map +1 -0
- package/dist/llm/providers/zhipu.d.ts +10 -0
- package/dist/llm/providers/zhipu.d.ts.map +1 -0
- package/dist/llm/providers/zhipu.js +81 -0
- package/dist/llm/providers/zhipu.js.map +1 -0
- package/dist/llm/router.d.ts +35 -0
- package/dist/llm/router.d.ts.map +1 -0
- package/dist/llm/router.js +109 -0
- package/dist/llm/router.js.map +1 -0
- package/dist/llm/types.d.ts +63 -0
- package/dist/llm/types.d.ts.map +1 -0
- package/dist/llm/types.js +38 -0
- package/dist/llm/types.js.map +1 -0
- package/dist/meta/index.d.ts +12 -0
- package/dist/meta/index.d.ts.map +1 -0
- package/dist/meta/index.js +8 -0
- package/dist/meta/index.js.map +1 -0
- package/dist/meta/metadata.json +46 -0
- package/dist/report/index.d.ts +20 -0
- package/dist/report/index.d.ts.map +1 -0
- package/dist/report/index.js +58 -0
- package/dist/report/index.js.map +1 -0
- package/dist/standards/index.d.ts +23 -0
- package/dist/standards/index.d.ts.map +1 -0
- package/dist/standards/index.js +89 -0
- package/dist/standards/index.js.map +1 -0
- package/dist/storage/index.d.ts +114 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +465 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/vision/index.d.ts +80 -0
- package/dist/vision/index.d.ts.map +1 -0
- package/dist/vision/index.js +298 -0
- package/dist/vision/index.js.map +1 -0
- package/dist/vision/parse.d.ts +20 -0
- package/dist/vision/parse.d.ts.map +1 -0
- package/dist/vision/parse.js +75 -0
- package/dist/vision/parse.js.map +1 -0
- package/package.json +55 -0
package/dist/db/redis.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Upstash Redis Usage Store — persistent metering for geotechCLI
|
|
3
|
+
//
|
|
4
|
+
// Replaces InMemoryUsageStore for production deployments.
|
|
5
|
+
// Uses Upstash Redis (HTTP-based) which works on Cloudflare Workers,
|
|
6
|
+
// Vercel Edge, and any serverless environment.
|
|
7
|
+
//
|
|
8
|
+
// Keys structure:
|
|
9
|
+
// usage:{identifier}:llmCalls — monthly LLM call count
|
|
10
|
+
// usage:{identifier}:visionCalls — monthly vision call count
|
|
11
|
+
// usage:{identifier}:agentCalls — monthly agent call count
|
|
12
|
+
// usage:{identifier}:meta — JSON: {tier, periodStart, lastCall}
|
|
13
|
+
// ratelimit:{ip} — sliding window counter
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
function getRedisConfig() {
|
|
16
|
+
const url = process.env.UPSTASH_REDIS_REST_URL ?? '';
|
|
17
|
+
const token = process.env.UPSTASH_REDIS_REST_TOKEN ?? '';
|
|
18
|
+
if (!url || !token) {
|
|
19
|
+
throw new Error('Missing Upstash Redis configuration. Set UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN.');
|
|
20
|
+
}
|
|
21
|
+
return { url, token };
|
|
22
|
+
}
|
|
23
|
+
async function redisCommand(args) {
|
|
24
|
+
const config = getRedisConfig();
|
|
25
|
+
try {
|
|
26
|
+
const res = await fetch(`${config.url}`, {
|
|
27
|
+
method: 'POST',
|
|
28
|
+
headers: {
|
|
29
|
+
Authorization: `Bearer ${config.token}`,
|
|
30
|
+
'Content-Type': 'application/json',
|
|
31
|
+
},
|
|
32
|
+
body: JSON.stringify(args),
|
|
33
|
+
signal: AbortSignal.timeout(5_000),
|
|
34
|
+
});
|
|
35
|
+
if (!res.ok) {
|
|
36
|
+
console.error(`[redis] Error ${res.status}: ${await res.text().catch(() => '')}`);
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const data = await res.json();
|
|
40
|
+
return data.result ?? null;
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
console.error(`[redis] Request failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Execute multiple Redis commands in a single HTTP request (pipeline).
|
|
49
|
+
*/
|
|
50
|
+
async function redisPipeline(commands) {
|
|
51
|
+
const config = getRedisConfig();
|
|
52
|
+
try {
|
|
53
|
+
const res = await fetch(`${config.url}/pipeline`, {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
headers: {
|
|
56
|
+
Authorization: `Bearer ${config.token}`,
|
|
57
|
+
'Content-Type': 'application/json',
|
|
58
|
+
},
|
|
59
|
+
body: JSON.stringify(commands),
|
|
60
|
+
signal: AbortSignal.timeout(5_000),
|
|
61
|
+
});
|
|
62
|
+
if (!res.ok) {
|
|
63
|
+
console.error(`[redis] Pipeline error ${res.status}`);
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
const data = await res.json();
|
|
67
|
+
return data.map((r) => r.result);
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
console.error(`[redis] Pipeline failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// Monthly period helpers
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
function getCurrentPeriodStart() {
|
|
78
|
+
const now = new Date();
|
|
79
|
+
return new Date(now.getFullYear(), now.getMonth(), 1).getTime();
|
|
80
|
+
}
|
|
81
|
+
function getCurrentPeriodKey() {
|
|
82
|
+
const now = new Date();
|
|
83
|
+
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
|
|
84
|
+
}
|
|
85
|
+
// TTL: 35 days (covers the full month + a few days buffer)
|
|
86
|
+
const PERIOD_TTL_SECONDS = 35 * 24 * 60 * 60;
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// RedisUsageStore — implements UsageStore interface
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
export class RedisUsageStore {
|
|
91
|
+
keyPrefix(identifier) {
|
|
92
|
+
const period = getCurrentPeriodKey();
|
|
93
|
+
return `usage:${period}:${identifier}`;
|
|
94
|
+
}
|
|
95
|
+
async get(identifier) {
|
|
96
|
+
const prefix = this.keyPrefix(identifier);
|
|
97
|
+
const results = await redisPipeline([
|
|
98
|
+
['GET', `${prefix}:llmCalls`],
|
|
99
|
+
['GET', `${prefix}:visionCalls`],
|
|
100
|
+
['GET', `${prefix}:agentCalls`],
|
|
101
|
+
['GET', `${prefix}:meta`],
|
|
102
|
+
]);
|
|
103
|
+
const llmCalls = Number(results[0]) || 0;
|
|
104
|
+
const visionCalls = Number(results[1]) || 0;
|
|
105
|
+
const agentCalls = Number(results[2]) || 0;
|
|
106
|
+
const metaStr = results[3];
|
|
107
|
+
let meta = null;
|
|
108
|
+
if (metaStr) {
|
|
109
|
+
try {
|
|
110
|
+
meta = JSON.parse(metaStr);
|
|
111
|
+
}
|
|
112
|
+
catch { /* ignore */ }
|
|
113
|
+
}
|
|
114
|
+
if (!meta && llmCalls === 0 && visionCalls === 0 && agentCalls === 0) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
identifier,
|
|
119
|
+
tier: meta?.tier ?? 'free',
|
|
120
|
+
llmCalls,
|
|
121
|
+
visionCalls,
|
|
122
|
+
agentCalls,
|
|
123
|
+
periodStart: meta?.periodStart ?? getCurrentPeriodStart(),
|
|
124
|
+
lastCallTimestamp: meta?.lastCallTimestamp ?? Date.now(),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
async set(identifier, record) {
|
|
128
|
+
const prefix = this.keyPrefix(identifier);
|
|
129
|
+
const meta = JSON.stringify({
|
|
130
|
+
tier: record.tier,
|
|
131
|
+
periodStart: record.periodStart,
|
|
132
|
+
lastCallTimestamp: record.lastCallTimestamp,
|
|
133
|
+
});
|
|
134
|
+
await redisPipeline([
|
|
135
|
+
['SET', `${prefix}:llmCalls`, record.llmCalls, 'EX', PERIOD_TTL_SECONDS],
|
|
136
|
+
['SET', `${prefix}:visionCalls`, record.visionCalls, 'EX', PERIOD_TTL_SECONDS],
|
|
137
|
+
['SET', `${prefix}:agentCalls`, record.agentCalls, 'EX', PERIOD_TTL_SECONDS],
|
|
138
|
+
['SET', `${prefix}:meta`, meta, 'EX', PERIOD_TTL_SECONDS],
|
|
139
|
+
]);
|
|
140
|
+
}
|
|
141
|
+
async increment(identifier, field) {
|
|
142
|
+
const prefix = this.keyPrefix(identifier);
|
|
143
|
+
const fieldKey = `${prefix}:${field}`;
|
|
144
|
+
const metaKey = `${prefix}:meta`;
|
|
145
|
+
const results = await redisPipeline([
|
|
146
|
+
['INCR', fieldKey],
|
|
147
|
+
['EXPIRE', fieldKey, PERIOD_TTL_SECONDS],
|
|
148
|
+
['GET', `${prefix}:llmCalls`],
|
|
149
|
+
['GET', `${prefix}:visionCalls`],
|
|
150
|
+
['GET', `${prefix}:agentCalls`],
|
|
151
|
+
['SET', metaKey, JSON.stringify({
|
|
152
|
+
tier: 'free',
|
|
153
|
+
periodStart: getCurrentPeriodStart(),
|
|
154
|
+
lastCallTimestamp: Date.now(),
|
|
155
|
+
}), 'EX', PERIOD_TTL_SECONDS],
|
|
156
|
+
]);
|
|
157
|
+
return {
|
|
158
|
+
identifier,
|
|
159
|
+
tier: 'free',
|
|
160
|
+
llmCalls: Number(results[2]) || 0,
|
|
161
|
+
visionCalls: Number(results[3]) || 0,
|
|
162
|
+
agentCalls: Number(results[4]) || 0,
|
|
163
|
+
periodStart: getCurrentPeriodStart(),
|
|
164
|
+
lastCallTimestamp: Date.now(),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
// IP Rate Limiter — sliding window using Redis
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
/**
|
|
172
|
+
* Check if an IP is rate-limited using Redis sliding window.
|
|
173
|
+
* Returns true if the IP should be blocked.
|
|
174
|
+
*/
|
|
175
|
+
export async function isIPRateLimitedRedis(ip, maxRequests = 15, windowMs = 60_000) {
|
|
176
|
+
// PRIVACY: Hash IP before using as Redis key — never store raw IPs
|
|
177
|
+
const encoder = new TextEncoder();
|
|
178
|
+
const hashBuf = await crypto.subtle.digest('SHA-256', encoder.encode(ip));
|
|
179
|
+
const ipHash = Array.from(new Uint8Array(hashBuf)).map(b => b.toString(16).padStart(2, '0')).join('').slice(0, 16);
|
|
180
|
+
const key = `ratelimit:${ipHash}`;
|
|
181
|
+
const now = Date.now();
|
|
182
|
+
const windowStart = now - windowMs;
|
|
183
|
+
// Sorted set: score = timestamp, member = unique request ID
|
|
184
|
+
const results = await redisPipeline([
|
|
185
|
+
// Remove entries outside the window
|
|
186
|
+
['ZREMRANGEBYSCORE', key, 0, windowStart],
|
|
187
|
+
// Count entries in the window
|
|
188
|
+
['ZCARD', key],
|
|
189
|
+
// Add current request
|
|
190
|
+
['ZADD', key, now, `${now}-${Math.random().toString(36).slice(2, 8)}`],
|
|
191
|
+
// Set TTL so the key auto-expires
|
|
192
|
+
['EXPIRE', key, Math.ceil(windowMs / 1000) + 5],
|
|
193
|
+
]);
|
|
194
|
+
const count = Number(results[1]) || 0;
|
|
195
|
+
return count >= maxRequests;
|
|
196
|
+
}
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
// Health check
|
|
199
|
+
// ---------------------------------------------------------------------------
|
|
200
|
+
export async function redisHealthCheck() {
|
|
201
|
+
const result = await redisCommand(['PING']);
|
|
202
|
+
return result === 'PONG';
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=redis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis.js","sourceRoot":"","sources":["../../src/db/redis.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export interface SupabaseConfig {
|
|
2
|
+
url: string;
|
|
3
|
+
serviceRoleKey: string;
|
|
4
|
+
}
|
|
5
|
+
interface QueryOptions {
|
|
6
|
+
table: string;
|
|
7
|
+
select?: string;
|
|
8
|
+
filters?: Array<{
|
|
9
|
+
column: string;
|
|
10
|
+
op: 'eq' | 'gt' | 'lt' | 'gte' | 'lte' | 'neq' | 'is';
|
|
11
|
+
value: string;
|
|
12
|
+
}>;
|
|
13
|
+
order?: {
|
|
14
|
+
column: string;
|
|
15
|
+
ascending?: boolean;
|
|
16
|
+
};
|
|
17
|
+
limit?: number;
|
|
18
|
+
single?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare function dbQuery<T>(opts: QueryOptions): Promise<{
|
|
21
|
+
data: T | null;
|
|
22
|
+
error: string | null;
|
|
23
|
+
}>;
|
|
24
|
+
export declare function dbInsert<T>(table: string, row: Record<string, unknown>): Promise<{
|
|
25
|
+
data: T | null;
|
|
26
|
+
error: string | null;
|
|
27
|
+
}>;
|
|
28
|
+
export declare function dbUpdate<T>(table: string, filters: Array<{
|
|
29
|
+
column: string;
|
|
30
|
+
op: 'eq';
|
|
31
|
+
value: string;
|
|
32
|
+
}>, updates: Record<string, unknown>): Promise<{
|
|
33
|
+
data: T | null;
|
|
34
|
+
error: string | null;
|
|
35
|
+
}>;
|
|
36
|
+
export declare function dbDelete<T>(table: string, filters: Array<{
|
|
37
|
+
column: string;
|
|
38
|
+
op: 'eq';
|
|
39
|
+
value: string;
|
|
40
|
+
}>): Promise<{
|
|
41
|
+
data: T | null;
|
|
42
|
+
error: string | null;
|
|
43
|
+
}>;
|
|
44
|
+
export declare function dbUpsert<T>(table: string, row: Record<string, unknown>, onConflict: string): Promise<{
|
|
45
|
+
data: T | null;
|
|
46
|
+
error: string | null;
|
|
47
|
+
}>;
|
|
48
|
+
export declare function dbRpc<T>(fnName: string, args: Record<string, unknown>): Promise<{
|
|
49
|
+
data: T | null;
|
|
50
|
+
error: string | null;
|
|
51
|
+
}>;
|
|
52
|
+
/**
|
|
53
|
+
* Health check — verifies Supabase connection works.
|
|
54
|
+
*/
|
|
55
|
+
export declare function dbHealthCheck(): Promise<boolean>;
|
|
56
|
+
export {};
|
|
57
|
+
//# sourceMappingURL=supabase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../src/db/supabase.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,UAAU,YAAY;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1G,KAAK,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AA8FD,wBAAsB,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAUtG;AAED,wBAAsB,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAGhI;AAED,wBAAsB,QAAQ,CAAC,CAAC,EAC9B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,KAAK,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EAC3D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC;IAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAQnD;AAED,wBAAsB,QAAQ,CAAC,CAAC,EAC9B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,KAAK,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,GAC1D,OAAO,CAAC;IAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAQnD;AAED,wBAAsB,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAKpJ;AAED,wBAAsB,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CA0B/H;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,CActD"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Supabase Client — lightweight REST client for geotechCLI
|
|
3
|
+
//
|
|
4
|
+
// Uses Supabase PostgREST API directly to avoid pulling in the full
|
|
5
|
+
// @supabase/supabase-js SDK (which adds ~200KB to the bundle).
|
|
6
|
+
// Only needs: SUPABASE_URL + SUPABASE_SERVICE_ROLE_KEY
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
function buildConfig() {
|
|
9
|
+
const url = process.env.SUPABASE_URL ?? '';
|
|
10
|
+
const key = process.env.SUPABASE_SERVICE_ROLE_KEY ?? '';
|
|
11
|
+
if (!url || !key) {
|
|
12
|
+
throw new Error('Missing Supabase configuration. Set SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY environment variables.');
|
|
13
|
+
}
|
|
14
|
+
return { url, serviceRoleKey: key };
|
|
15
|
+
}
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Core HTTP methods
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
async function supabaseRequest(method, path, config, body, headers) {
|
|
20
|
+
const url = `${config.url}/rest/v1/${path}`;
|
|
21
|
+
const reqHeaders = {
|
|
22
|
+
'apikey': config.serviceRoleKey,
|
|
23
|
+
'Authorization': `Bearer ${config.serviceRoleKey}`,
|
|
24
|
+
'Content-Type': 'application/json',
|
|
25
|
+
'Prefer': method === 'POST' ? 'return=representation' : method === 'PATCH' ? 'return=representation' : 'return=minimal',
|
|
26
|
+
...headers,
|
|
27
|
+
};
|
|
28
|
+
try {
|
|
29
|
+
const res = await fetch(url, {
|
|
30
|
+
method,
|
|
31
|
+
headers: reqHeaders,
|
|
32
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
33
|
+
signal: AbortSignal.timeout(10_000),
|
|
34
|
+
});
|
|
35
|
+
if (!res.ok) {
|
|
36
|
+
const errText = await res.text().catch(() => 'Unknown error');
|
|
37
|
+
return { data: null, error: `Supabase ${res.status}: ${errText.slice(0, 200)}` };
|
|
38
|
+
}
|
|
39
|
+
if (res.status === 204 || method === 'DELETE') {
|
|
40
|
+
return { data: null, error: null };
|
|
41
|
+
}
|
|
42
|
+
const data = await res.json();
|
|
43
|
+
return { data: data, error: null };
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
47
|
+
return { data: null, error: `Supabase request failed: ${msg}` };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Query builder
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
function buildQueryString(opts) {
|
|
54
|
+
const params = new URLSearchParams();
|
|
55
|
+
if (opts.select) {
|
|
56
|
+
params.set('select', opts.select);
|
|
57
|
+
}
|
|
58
|
+
if (opts.filters) {
|
|
59
|
+
for (const f of opts.filters) {
|
|
60
|
+
params.set(f.column, `${f.op}.${f.value}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (opts.order) {
|
|
64
|
+
params.set('order', `${opts.order.column}.${opts.order.ascending ? 'asc' : 'desc'}`);
|
|
65
|
+
}
|
|
66
|
+
if (opts.limit) {
|
|
67
|
+
params.set('limit', String(opts.limit));
|
|
68
|
+
}
|
|
69
|
+
const qs = params.toString();
|
|
70
|
+
return `${opts.table}${qs ? `?${qs}` : ''}`;
|
|
71
|
+
}
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Public API
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
export async function dbQuery(opts) {
|
|
76
|
+
const config = buildConfig();
|
|
77
|
+
const path = buildQueryString(opts);
|
|
78
|
+
const headers = {};
|
|
79
|
+
if (opts.single) {
|
|
80
|
+
headers['Accept'] = 'application/vnd.pgrst.object+json';
|
|
81
|
+
}
|
|
82
|
+
return supabaseRequest('GET', path, config, undefined, headers);
|
|
83
|
+
}
|
|
84
|
+
export async function dbInsert(table, row) {
|
|
85
|
+
const config = buildConfig();
|
|
86
|
+
return supabaseRequest('POST', table, config, row);
|
|
87
|
+
}
|
|
88
|
+
export async function dbUpdate(table, filters, updates) {
|
|
89
|
+
const config = buildConfig();
|
|
90
|
+
const params = new URLSearchParams();
|
|
91
|
+
for (const f of filters) {
|
|
92
|
+
params.set(f.column, `${f.op}.${f.value}`);
|
|
93
|
+
}
|
|
94
|
+
const path = `${table}?${params.toString()}`;
|
|
95
|
+
return supabaseRequest('PATCH', path, config, updates);
|
|
96
|
+
}
|
|
97
|
+
export async function dbDelete(table, filters) {
|
|
98
|
+
const config = buildConfig();
|
|
99
|
+
const params = new URLSearchParams();
|
|
100
|
+
for (const f of filters) {
|
|
101
|
+
params.set(f.column, `${f.op}.${f.value}`);
|
|
102
|
+
}
|
|
103
|
+
const path = `${table}?${params.toString()}`;
|
|
104
|
+
return supabaseRequest('DELETE', path, config);
|
|
105
|
+
}
|
|
106
|
+
export async function dbUpsert(table, row, onConflict) {
|
|
107
|
+
const config = buildConfig();
|
|
108
|
+
return supabaseRequest('POST', table, config, row, {
|
|
109
|
+
'Prefer': 'resolution=merge-duplicates,return=representation',
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
export async function dbRpc(fnName, args) {
|
|
113
|
+
const config = buildConfig();
|
|
114
|
+
const url = `${config.url}/rest/v1/rpc/${fnName}`;
|
|
115
|
+
try {
|
|
116
|
+
const res = await fetch(url, {
|
|
117
|
+
method: 'POST',
|
|
118
|
+
headers: {
|
|
119
|
+
'apikey': config.serviceRoleKey,
|
|
120
|
+
'Authorization': `Bearer ${config.serviceRoleKey}`,
|
|
121
|
+
'Content-Type': 'application/json',
|
|
122
|
+
},
|
|
123
|
+
body: JSON.stringify(args),
|
|
124
|
+
signal: AbortSignal.timeout(10_000),
|
|
125
|
+
});
|
|
126
|
+
if (!res.ok) {
|
|
127
|
+
const errText = await res.text().catch(() => '');
|
|
128
|
+
return { data: null, error: `RPC ${fnName} failed (${res.status}): ${errText.slice(0, 200)}` };
|
|
129
|
+
}
|
|
130
|
+
const data = await res.json();
|
|
131
|
+
return { data: data, error: null };
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
return { data: null, error: err instanceof Error ? err.message : String(err) };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Health check — verifies Supabase connection works.
|
|
139
|
+
*/
|
|
140
|
+
export async function dbHealthCheck() {
|
|
141
|
+
try {
|
|
142
|
+
const config = buildConfig();
|
|
143
|
+
const res = await fetch(`${config.url}/rest/v1/`, {
|
|
144
|
+
headers: {
|
|
145
|
+
'apikey': config.serviceRoleKey,
|
|
146
|
+
'Authorization': `Bearer ${config.serviceRoleKey}`,
|
|
147
|
+
},
|
|
148
|
+
signal: AbortSignal.timeout(5_000),
|
|
149
|
+
});
|
|
150
|
+
return res.ok;
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=supabase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supabase.js","sourceRoot":"","sources":["../../src/db/supabase.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { UserTier } from '../llm/types.js';
|
|
2
|
+
export interface GeotechUser {
|
|
3
|
+
id: string;
|
|
4
|
+
email: string;
|
|
5
|
+
geotech_key: string;
|
|
6
|
+
tier: UserTier;
|
|
7
|
+
stripe_customer_id: string | null;
|
|
8
|
+
stripe_subscription_id: string | null;
|
|
9
|
+
stripe_price_id: string | null;
|
|
10
|
+
subscription_status: 'none' | 'active' | 'past_due' | 'canceled' | 'trialing';
|
|
11
|
+
subscription_end_at: string | null;
|
|
12
|
+
created_at: string;
|
|
13
|
+
updated_at: string;
|
|
14
|
+
}
|
|
15
|
+
export interface CreateUserInput {
|
|
16
|
+
email: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function generateGeotechKey(): string;
|
|
19
|
+
export declare function getUserByKey(geotechKey: string): Promise<GeotechUser | null>;
|
|
20
|
+
export declare function getUserByEmail(email: string): Promise<GeotechUser | null>;
|
|
21
|
+
export declare function getUserById(userId: string): Promise<GeotechUser | null>;
|
|
22
|
+
export declare function getUserByStripeCustomer(stripeCustomerId: string): Promise<GeotechUser | null>;
|
|
23
|
+
export declare function createUser(input: CreateUserInput): Promise<{
|
|
24
|
+
user: GeotechUser | null;
|
|
25
|
+
error: string | null;
|
|
26
|
+
}>;
|
|
27
|
+
export declare function updateSubscription(userId: string, updates: {
|
|
28
|
+
tier: UserTier;
|
|
29
|
+
stripe_subscription_id?: string | null;
|
|
30
|
+
stripe_price_id?: string | null;
|
|
31
|
+
subscription_status: 'active' | 'past_due' | 'canceled' | 'trialing' | 'none';
|
|
32
|
+
subscription_end_at?: string | null;
|
|
33
|
+
}): Promise<{
|
|
34
|
+
error: string | null;
|
|
35
|
+
}>;
|
|
36
|
+
export declare function linkStripeCustomer(userId: string, stripeCustomerId: string): Promise<{
|
|
37
|
+
error: string | null;
|
|
38
|
+
}>;
|
|
39
|
+
export declare function regenerateKey(userId: string): Promise<{
|
|
40
|
+
key: string | null;
|
|
41
|
+
error: string | null;
|
|
42
|
+
}>;
|
|
43
|
+
/**
|
|
44
|
+
* Delete a user and all their data (GDPR right to erasure).
|
|
45
|
+
*/
|
|
46
|
+
export declare function deleteUser(userId: string): Promise<{
|
|
47
|
+
error: string | null;
|
|
48
|
+
}>;
|
|
49
|
+
export declare function resolveEffectiveTier(user: GeotechUser): UserTier;
|
|
50
|
+
//# sourceMappingURL=users.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/db/users.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAMhD,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,QAAQ,CAAC;IACf,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,mBAAmB,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,CAAC;IAC9E,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;CACf;AAMD,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAMD,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAclF;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAU/E;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAU7E;AAED,wBAAsB,uBAAuB,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAUnG;AAMD,wBAAsB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAkBpH;AAMD,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE;IACP,IAAI,EAAE,QAAQ,CAAC;IACf,sBAAsB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,mBAAmB,EAAE,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CAAC;IAC9E,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC,GACA,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAMnC;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAMnC;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAQzG;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAKlF;AAMD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,GAAG,QAAQ,CAahE"}
|
package/dist/db/users.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// User Management — Supabase-backed, privacy-first
|
|
3
|
+
//
|
|
4
|
+
// PRIVACY GUARANTEES:
|
|
5
|
+
// - We NEVER store user LLM API keys (OpenAI, Anthropic, HF tokens, etc.)
|
|
6
|
+
// - We NEVER log or store the content of prompts or responses
|
|
7
|
+
// - We NEVER store IP addresses or device fingerprints in the database
|
|
8
|
+
// - We store ONLY: email (account recovery), geotech_key (our auth token),
|
|
9
|
+
// tier, and Stripe billing IDs
|
|
10
|
+
// - User's LLM keys stay on their machine (~/.geotechcli/config.json)
|
|
11
|
+
// and are sent DIRECTLY to the LLM provider — never to our servers
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
import { randomBytes } from 'node:crypto';
|
|
14
|
+
import { dbQuery, dbInsert, dbUpdate, dbDelete } from './supabase.js';
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Key generation — our auth token, NOT an LLM key
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
export function generateGeotechKey() {
|
|
19
|
+
return `gtp_${randomBytes(16).toString('hex')}`;
|
|
20
|
+
}
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// User lookup — hot path (every proxy request)
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
export async function getUserByKey(geotechKey) {
|
|
25
|
+
if (!geotechKey || !geotechKey.startsWith('gtp_') || geotechKey.length < 20) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const { data, error } = await dbQuery({
|
|
29
|
+
table: 'users',
|
|
30
|
+
select: '*',
|
|
31
|
+
filters: [{ column: 'geotech_key', op: 'eq', value: geotechKey }],
|
|
32
|
+
limit: 1,
|
|
33
|
+
});
|
|
34
|
+
if (error || !data || data.length === 0)
|
|
35
|
+
return null;
|
|
36
|
+
return data[0];
|
|
37
|
+
}
|
|
38
|
+
export async function getUserByEmail(email) {
|
|
39
|
+
const { data, error } = await dbQuery({
|
|
40
|
+
table: 'users',
|
|
41
|
+
select: '*',
|
|
42
|
+
filters: [{ column: 'email', op: 'eq', value: email }],
|
|
43
|
+
limit: 1,
|
|
44
|
+
});
|
|
45
|
+
if (error || !data || data.length === 0)
|
|
46
|
+
return null;
|
|
47
|
+
return data[0];
|
|
48
|
+
}
|
|
49
|
+
export async function getUserById(userId) {
|
|
50
|
+
const { data, error } = await dbQuery({
|
|
51
|
+
table: 'users',
|
|
52
|
+
select: '*',
|
|
53
|
+
filters: [{ column: 'id', op: 'eq', value: userId }],
|
|
54
|
+
limit: 1,
|
|
55
|
+
});
|
|
56
|
+
if (error || !data || data.length === 0)
|
|
57
|
+
return null;
|
|
58
|
+
return data[0];
|
|
59
|
+
}
|
|
60
|
+
export async function getUserByStripeCustomer(stripeCustomerId) {
|
|
61
|
+
const { data, error } = await dbQuery({
|
|
62
|
+
table: 'users',
|
|
63
|
+
select: '*',
|
|
64
|
+
filters: [{ column: 'stripe_customer_id', op: 'eq', value: stripeCustomerId }],
|
|
65
|
+
limit: 1,
|
|
66
|
+
});
|
|
67
|
+
if (error || !data || data.length === 0)
|
|
68
|
+
return null;
|
|
69
|
+
return data[0];
|
|
70
|
+
}
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// User creation
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
export async function createUser(input) {
|
|
75
|
+
const geotechKey = generateGeotechKey();
|
|
76
|
+
const { data, error } = await dbInsert('users', {
|
|
77
|
+
email: input.email,
|
|
78
|
+
geotech_key: geotechKey,
|
|
79
|
+
tier: 'free',
|
|
80
|
+
subscription_status: 'none',
|
|
81
|
+
});
|
|
82
|
+
if (error) {
|
|
83
|
+
if (error.includes('duplicate') || error.includes('23505')) {
|
|
84
|
+
return { user: null, error: 'A user with this email already exists.' };
|
|
85
|
+
}
|
|
86
|
+
return { user: null, error };
|
|
87
|
+
}
|
|
88
|
+
return { user: data && data.length > 0 ? data[0] : null, error: null };
|
|
89
|
+
}
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// Subscription management (called from Stripe webhooks only)
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
export async function updateSubscription(userId, updates) {
|
|
94
|
+
const { error } = await dbUpdate('users', [{ column: 'id', op: 'eq', value: userId }], updates);
|
|
95
|
+
return { error };
|
|
96
|
+
}
|
|
97
|
+
export async function linkStripeCustomer(userId, stripeCustomerId) {
|
|
98
|
+
const { error } = await dbUpdate('users', [{ column: 'id', op: 'eq', value: userId }], { stripe_customer_id: stripeCustomerId });
|
|
99
|
+
return { error };
|
|
100
|
+
}
|
|
101
|
+
export async function regenerateKey(userId) {
|
|
102
|
+
const newKey = generateGeotechKey();
|
|
103
|
+
const { error } = await dbUpdate('users', [{ column: 'id', op: 'eq', value: userId }], { geotech_key: newKey });
|
|
104
|
+
if (error)
|
|
105
|
+
return { key: null, error };
|
|
106
|
+
return { key: newKey, error: null };
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Delete a user and all their data (GDPR right to erasure).
|
|
110
|
+
*/
|
|
111
|
+
export async function deleteUser(userId) {
|
|
112
|
+
const { error } = await dbDelete('users', [{ column: 'id', op: 'eq', value: userId }]);
|
|
113
|
+
return { error: error ?? null };
|
|
114
|
+
}
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
// Tier resolution
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
export function resolveEffectiveTier(user) {
|
|
119
|
+
if (user.subscription_status === 'active' || user.subscription_status === 'trialing') {
|
|
120
|
+
return user.tier;
|
|
121
|
+
}
|
|
122
|
+
if (user.subscription_status === 'past_due') {
|
|
123
|
+
return user.tier; // Grace period
|
|
124
|
+
}
|
|
125
|
+
if (user.subscription_status === 'canceled' && user.subscription_end_at) {
|
|
126
|
+
if (new Date(user.subscription_end_at) > new Date()) {
|
|
127
|
+
return user.tier; // Canceled but not yet expired
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return 'free';
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=users.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"users.js","sourceRoot":"","sources":["../../src/db/users.ts"],"names":[],"mappings":"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"}
|