vibecodingmachine-core 2025.12.6-1702 → 2025.12.22-2230
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/scripts/setup-database.js +108 -0
- package/src/compliance/compliance-manager.js +249 -0
- package/src/compliance/compliance-prompt.js +183 -0
- package/src/database/migrations.js +289 -0
- package/src/database/user-database-client.js +266 -0
- package/src/database/user-schema.js +118 -0
- package/src/ide-integration/applescript-manager.cjs +73 -127
- package/src/ide-integration/applescript-manager.js +62 -12
- package/src/ide-integration/claude-code-cli-manager.cjs +120 -1
- package/src/ide-integration/provider-manager.cjs +67 -1
- package/src/index.js +4 -0
- package/src/llm/direct-llm-manager.cjs +110 -73
- package/src/quota-management/index.js +108 -0
- package/src/sync/sync-engine.js +32 -10
- package/src/utils/download-with-progress.js +92 -0
- package/src/utils/electron-update-checker.js +7 -0
- package/src/utils/env-helpers.js +54 -0
- package/src/utils/requirement-helpers.js +745 -49
- package/src/utils/requirements-parser.js +21 -7
- package/src/utils/update-checker.js +7 -0
- package/test-quota-system.js +67 -0
- package/test-requirement-stats.js +66 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database Migration Scripts for Enhanced User Tracking
|
|
3
|
+
*
|
|
4
|
+
* This module handles creating and updating DynamoDB tables for the
|
|
5
|
+
* enhanced user tracking system.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb')
|
|
9
|
+
const {
|
|
10
|
+
CreateTableCommand,
|
|
11
|
+
DescribeTableCommand,
|
|
12
|
+
UpdateTableCommand,
|
|
13
|
+
ListTablesCommand
|
|
14
|
+
} = require('@aws-sdk/client-dynamodb')
|
|
15
|
+
|
|
16
|
+
class DatabaseMigrations {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.client = new DynamoDBClient({
|
|
19
|
+
region: process.env.AWS_REGION || 'us-east-1'
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Run all migrations to set up the enhanced user tracking system
|
|
25
|
+
*/
|
|
26
|
+
async runMigrations() {
|
|
27
|
+
console.log('🔄 Running database migrations...')
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
await this.createUsersTable()
|
|
31
|
+
await this.createComputersTable()
|
|
32
|
+
await this.createProjectsTable()
|
|
33
|
+
await this.createActivityTable()
|
|
34
|
+
|
|
35
|
+
console.log('✅ All migrations completed successfully')
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error('❌ Migration failed:', error)
|
|
38
|
+
throw error
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Create users table with enhanced schema
|
|
44
|
+
*/
|
|
45
|
+
async createUsersTable() {
|
|
46
|
+
const tableName = 'vibecodingmachine-users'
|
|
47
|
+
|
|
48
|
+
if (await this.tableExists(tableName)) {
|
|
49
|
+
console.log(`📋 Table ${tableName} already exists`)
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const params = {
|
|
54
|
+
TableName: tableName,
|
|
55
|
+
KeySchema: [
|
|
56
|
+
{ AttributeName: 'userId', KeyType: 'HASH' }
|
|
57
|
+
],
|
|
58
|
+
AttributeDefinitions: [
|
|
59
|
+
{ AttributeName: 'userId', AttributeType: 'S' },
|
|
60
|
+
{ AttributeName: 'email', AttributeType: 'S' },
|
|
61
|
+
{ AttributeName: 'registrationDate', AttributeType: 'N' }
|
|
62
|
+
],
|
|
63
|
+
GlobalSecondaryIndexes: [
|
|
64
|
+
{
|
|
65
|
+
IndexName: 'EmailIndex',
|
|
66
|
+
KeySchema: [
|
|
67
|
+
{ AttributeName: 'email', KeyType: 'HASH' }
|
|
68
|
+
],
|
|
69
|
+
Projection: { ProjectionType: 'ALL' },
|
|
70
|
+
BillingMode: 'PAY_PER_REQUEST'
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
IndexName: 'RegistrationDateIndex',
|
|
74
|
+
KeySchema: [
|
|
75
|
+
{ AttributeName: 'registrationDate', KeyType: 'HASH' }
|
|
76
|
+
],
|
|
77
|
+
Projection: { ProjectionType: 'ALL' },
|
|
78
|
+
BillingMode: 'PAY_PER_REQUEST'
|
|
79
|
+
}
|
|
80
|
+
],
|
|
81
|
+
BillingMode: 'PAY_PER_REQUEST',
|
|
82
|
+
Tags: [
|
|
83
|
+
{ Key: 'Application', Value: 'VibeCodingMachine' },
|
|
84
|
+
{ Key: 'Environment', Value: process.env.NODE_ENV || 'development' }
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
await this.client.send(new CreateTableCommand(params))
|
|
89
|
+
console.log(`✅ Created table: ${tableName}`)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Create computers table for tracking user devices
|
|
94
|
+
*/
|
|
95
|
+
async createComputersTable() {
|
|
96
|
+
const tableName = 'vibecodingmachine-computers'
|
|
97
|
+
|
|
98
|
+
if (await this.tableExists(tableName)) {
|
|
99
|
+
console.log(`📋 Table ${tableName} already exists`)
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const params = {
|
|
104
|
+
TableName: tableName,
|
|
105
|
+
KeySchema: [
|
|
106
|
+
{ AttributeName: 'computerId', KeyType: 'HASH' }
|
|
107
|
+
],
|
|
108
|
+
AttributeDefinitions: [
|
|
109
|
+
{ AttributeName: 'computerId', AttributeType: 'S' },
|
|
110
|
+
{ AttributeName: 'userId', AttributeType: 'S' },
|
|
111
|
+
{ AttributeName: 'lastSeen', AttributeType: 'N' }
|
|
112
|
+
],
|
|
113
|
+
GlobalSecondaryIndexes: [
|
|
114
|
+
{
|
|
115
|
+
IndexName: 'UserIdIndex',
|
|
116
|
+
KeySchema: [
|
|
117
|
+
{ AttributeName: 'userId', KeyType: 'HASH' },
|
|
118
|
+
{ AttributeName: 'lastSeen', KeyType: 'RANGE' }
|
|
119
|
+
],
|
|
120
|
+
Projection: { ProjectionType: 'ALL' },
|
|
121
|
+
BillingMode: 'PAY_PER_REQUEST'
|
|
122
|
+
}
|
|
123
|
+
],
|
|
124
|
+
BillingMode: 'PAY_PER_REQUEST',
|
|
125
|
+
Tags: [
|
|
126
|
+
{ Key: 'Application', Value: 'VibeCodingMachine' },
|
|
127
|
+
{ Key: 'Environment', Value: process.env.NODE_ENV || 'development' }
|
|
128
|
+
]
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
await this.client.send(new CreateTableCommand(params))
|
|
132
|
+
console.log(`✅ Created table: ${tableName}`)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Create projects table for tracking user projects
|
|
137
|
+
*/
|
|
138
|
+
async createProjectsTable() {
|
|
139
|
+
const tableName = 'vibecodingmachine-projects'
|
|
140
|
+
|
|
141
|
+
if (await this.tableExists(tableName)) {
|
|
142
|
+
console.log(`📋 Table ${tableName} already exists`)
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const params = {
|
|
147
|
+
TableName: tableName,
|
|
148
|
+
KeySchema: [
|
|
149
|
+
{ AttributeName: 'projectId', KeyType: 'HASH' }
|
|
150
|
+
],
|
|
151
|
+
AttributeDefinitions: [
|
|
152
|
+
{ AttributeName: 'projectId', AttributeType: 'S' },
|
|
153
|
+
{ AttributeName: 'userId', AttributeType: 'S' },
|
|
154
|
+
{ AttributeName: 'createdAt', AttributeType: 'N' }
|
|
155
|
+
],
|
|
156
|
+
GlobalSecondaryIndexes: [
|
|
157
|
+
{
|
|
158
|
+
IndexName: 'UserIdIndex',
|
|
159
|
+
KeySchema: [
|
|
160
|
+
{ AttributeName: 'userId', KeyType: 'HASH' },
|
|
161
|
+
{ AttributeName: 'createdAt', KeyType: 'RANGE' }
|
|
162
|
+
],
|
|
163
|
+
Projection: { ProjectionType: 'ALL' },
|
|
164
|
+
BillingMode: 'PAY_PER_REQUEST'
|
|
165
|
+
}
|
|
166
|
+
],
|
|
167
|
+
BillingMode: 'PAY_PER_REQUEST',
|
|
168
|
+
Tags: [
|
|
169
|
+
{ Key: 'Application', Value: 'VibeCodingMachine' },
|
|
170
|
+
{ Key: 'Environment', Value: process.env.NODE_ENV || 'development' }
|
|
171
|
+
]
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
await this.client.send(new CreateTableCommand(params))
|
|
175
|
+
console.log(`✅ Created table: ${tableName}`)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Create activity table for detailed usage tracking
|
|
180
|
+
*/
|
|
181
|
+
async createActivityTable() {
|
|
182
|
+
const tableName = 'vibecodingmachine-activity'
|
|
183
|
+
|
|
184
|
+
if (await this.tableExists(tableName)) {
|
|
185
|
+
console.log(`📋 Table ${tableName} already exists`)
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const params = {
|
|
190
|
+
TableName: tableName,
|
|
191
|
+
KeySchema: [
|
|
192
|
+
{ AttributeName: 'activityId', KeyType: 'HASH' }
|
|
193
|
+
],
|
|
194
|
+
AttributeDefinitions: [
|
|
195
|
+
{ AttributeName: 'activityId', AttributeType: 'S' },
|
|
196
|
+
{ AttributeName: 'userId', AttributeType: 'S' },
|
|
197
|
+
{ AttributeName: 'createdAt', AttributeType: 'N' }
|
|
198
|
+
],
|
|
199
|
+
GlobalSecondaryIndexes: [
|
|
200
|
+
{
|
|
201
|
+
IndexName: 'UserIdIndex',
|
|
202
|
+
KeySchema: [
|
|
203
|
+
{ AttributeName: 'userId', KeyType: 'HASH' },
|
|
204
|
+
{ AttributeName: 'createdAt', KeyType: 'RANGE' }
|
|
205
|
+
],
|
|
206
|
+
Projection: { ProjectionType: 'ALL' },
|
|
207
|
+
BillingMode: 'PAY_PER_REQUEST'
|
|
208
|
+
}
|
|
209
|
+
],
|
|
210
|
+
BillingMode: 'PAY_PER_REQUEST',
|
|
211
|
+
TimeToLiveSpecification: {
|
|
212
|
+
AttributeName: 'ttl',
|
|
213
|
+
Enabled: true
|
|
214
|
+
},
|
|
215
|
+
Tags: [
|
|
216
|
+
{ Key: 'Application', Value: 'VibeCodingMachine' },
|
|
217
|
+
{ Key: 'Environment', Value: process.env.NODE_ENV || 'development' }
|
|
218
|
+
]
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
await this.client.send(new CreateTableCommand(params))
|
|
222
|
+
console.log(`✅ Created table: ${tableName}`)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Check if a table exists
|
|
227
|
+
*/
|
|
228
|
+
async tableExists(tableName) {
|
|
229
|
+
try {
|
|
230
|
+
await this.client.send(new DescribeTableCommand({ TableName: tableName }))
|
|
231
|
+
return true
|
|
232
|
+
} catch (error) {
|
|
233
|
+
if (error.name === 'ResourceNotFoundException') {
|
|
234
|
+
return false
|
|
235
|
+
}
|
|
236
|
+
throw error
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* List all VibeCodingMachine tables
|
|
242
|
+
*/
|
|
243
|
+
async listTables() {
|
|
244
|
+
const response = await this.client.send(new ListTablesCommand({}))
|
|
245
|
+
return response.TableNames.filter(name => name.startsWith('vibecodingmachine-'))
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Get table status
|
|
250
|
+
*/
|
|
251
|
+
async getTableStatus(tableName) {
|
|
252
|
+
try {
|
|
253
|
+
const response = await this.client.send(new DescribeTableCommand({ TableName: tableName }))
|
|
254
|
+
return {
|
|
255
|
+
name: tableName,
|
|
256
|
+
status: response.Table.TableStatus,
|
|
257
|
+
itemCount: response.Table.ItemCount,
|
|
258
|
+
sizeBytes: response.Table.TableSizeBytes,
|
|
259
|
+
created: response.Table.CreationDateTime
|
|
260
|
+
}
|
|
261
|
+
} catch (error) {
|
|
262
|
+
return {
|
|
263
|
+
name: tableName,
|
|
264
|
+
status: 'NOT_FOUND',
|
|
265
|
+
error: error.message
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Get status of all tables
|
|
272
|
+
*/
|
|
273
|
+
async getAllTableStatus() {
|
|
274
|
+
const tableNames = [
|
|
275
|
+
'vibecodingmachine-users',
|
|
276
|
+
'vibecodingmachine-computers',
|
|
277
|
+
'vibecodingmachine-projects',
|
|
278
|
+
'vibecodingmachine-activity'
|
|
279
|
+
]
|
|
280
|
+
|
|
281
|
+
const statuses = await Promise.all(
|
|
282
|
+
tableNames.map(name => this.getTableStatus(name))
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
return statuses
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
module.exports = DatabaseMigrations
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side User Database for VibeCodingMachine
|
|
3
|
+
*
|
|
4
|
+
* This version makes HTTP requests to the API server instead of
|
|
5
|
+
* directly accessing AWS DynamoDB.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const os = require('os');
|
|
9
|
+
const crypto = require('crypto');
|
|
10
|
+
|
|
11
|
+
// Use node-fetch for older Node.js versions that don't have fetch built-in
|
|
12
|
+
let fetch;
|
|
13
|
+
try {
|
|
14
|
+
fetch = globalThis.fetch || require('node-fetch');
|
|
15
|
+
} catch (err) {
|
|
16
|
+
console.warn('UserDatabaseClient: fetch not available, API requests will fail');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
class UserDatabaseClient {
|
|
20
|
+
constructor(apiBaseUrl = null) {
|
|
21
|
+
// Default to production API server, or use provided URL
|
|
22
|
+
this.apiBaseUrl = apiBaseUrl || process.env.VIBECODINGMACHINE_API_URL || 'https://api.vibecodingmachine.com';
|
|
23
|
+
this.authToken = null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Set the authentication token for API requests
|
|
28
|
+
*/
|
|
29
|
+
setAuthToken(token) {
|
|
30
|
+
this.authToken = token;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Make authenticated API request
|
|
35
|
+
*/
|
|
36
|
+
async apiRequest(endpoint, options = {}) {
|
|
37
|
+
const url = `${this.apiBaseUrl}${endpoint}`;
|
|
38
|
+
const headers = {
|
|
39
|
+
'Content-Type': 'application/json',
|
|
40
|
+
...options.headers
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
if (this.authToken) {
|
|
44
|
+
headers.Authorization = `Bearer ${this.authToken}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const response = await fetch(url, {
|
|
48
|
+
...options,
|
|
49
|
+
headers
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (!response.ok) {
|
|
53
|
+
const error = await response.json().catch(() => ({ error: 'Network error' }));
|
|
54
|
+
throw new Error(error.error || `HTTP ${response.status}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return response.json();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Register or update user in the database
|
|
62
|
+
*/
|
|
63
|
+
async registerUser(userInfo) {
|
|
64
|
+
if (!this.authToken) {
|
|
65
|
+
console.warn('UserDatabase: No auth token, skipping user registration');
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const result = await this.apiRequest('/api/users/register', {
|
|
71
|
+
method: 'POST',
|
|
72
|
+
body: JSON.stringify(userInfo)
|
|
73
|
+
});
|
|
74
|
+
return result.user;
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.warn('UserDatabase: registerUser failed:', error.message);
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Register or update computer information
|
|
83
|
+
*/
|
|
84
|
+
async registerComputer(userId, computerInfo = {}) {
|
|
85
|
+
if (!this.authToken) {
|
|
86
|
+
console.warn('UserDatabase: No auth token, skipping computer registration');
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
// Collect local computer info
|
|
92
|
+
const localInfo = {
|
|
93
|
+
hostname: os.hostname(),
|
|
94
|
+
platform: os.platform(),
|
|
95
|
+
arch: os.arch(),
|
|
96
|
+
release: os.release(),
|
|
97
|
+
cpus: os.cpus().length,
|
|
98
|
+
totalMemory: Math.round(os.totalmem() / (1024 * 1024 * 1024)), // GB
|
|
99
|
+
ipAddress: this.getLocalIP(),
|
|
100
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
101
|
+
...computerInfo
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const result = await this.apiRequest('/api/computers/register', {
|
|
105
|
+
method: 'POST',
|
|
106
|
+
body: JSON.stringify(localInfo)
|
|
107
|
+
});
|
|
108
|
+
return result.computer;
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.warn('UserDatabase: registerComputer failed:', error.message);
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Register a new project
|
|
117
|
+
*/
|
|
118
|
+
async registerProject(userId, projectInfo) {
|
|
119
|
+
if (!this.authToken) {
|
|
120
|
+
console.warn('UserDatabase: No auth token, skipping project registration');
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const result = await this.apiRequest('/api/projects/register', {
|
|
126
|
+
method: 'POST',
|
|
127
|
+
body: JSON.stringify(projectInfo)
|
|
128
|
+
});
|
|
129
|
+
return result.project;
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.warn('UserDatabase: registerProject failed:', error.message);
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Update user's terms and privacy acceptance
|
|
138
|
+
*/
|
|
139
|
+
async updateComplianceStatus(userId, complianceData) {
|
|
140
|
+
if (!this.authToken) {
|
|
141
|
+
console.warn('UserDatabase: updateComplianceStatus skipped (no auth token)');
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
await this.apiRequest('/api/users/compliance', {
|
|
147
|
+
method: 'POST',
|
|
148
|
+
body: JSON.stringify(complianceData)
|
|
149
|
+
});
|
|
150
|
+
} catch (error) {
|
|
151
|
+
console.warn('UserDatabase: updateComplianceStatus failed:', error.message);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Track user activity and interface usage
|
|
157
|
+
*/
|
|
158
|
+
async trackActivity(userId, activityData) {
|
|
159
|
+
if (!this.authToken) {
|
|
160
|
+
console.warn('UserDatabase: trackActivity skipped (no auth token)');
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
await this.apiRequest('/api/users/activity', {
|
|
166
|
+
method: 'POST',
|
|
167
|
+
body: JSON.stringify(activityData)
|
|
168
|
+
});
|
|
169
|
+
} catch (error) {
|
|
170
|
+
console.warn('UserDatabase: trackActivity failed:', error.message);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Get user by ID (not implemented client-side for security)
|
|
176
|
+
*/
|
|
177
|
+
async getUser(userId) {
|
|
178
|
+
if (!this.authToken) {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const result = await this.apiRequest('/api/users/me');
|
|
184
|
+
return result.user;
|
|
185
|
+
} catch (error) {
|
|
186
|
+
console.warn('UserDatabase: getUser failed:', error.message);
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Get user by email
|
|
193
|
+
*/
|
|
194
|
+
async getUserByEmail(email) {
|
|
195
|
+
// Client-side implementation just calls getUser
|
|
196
|
+
return this.getUser(this.generateUserId(email));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Update user activity timestamp (handled by trackActivity)
|
|
201
|
+
*/
|
|
202
|
+
async updateUserActivity(userId, updates) {
|
|
203
|
+
// This is handled by trackActivity on the client side
|
|
204
|
+
console.warn('UserDatabase: updateUserActivity not implemented on client side');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Check if user has accepted terms and privacy policy
|
|
209
|
+
*/
|
|
210
|
+
async checkCompliance(userId) {
|
|
211
|
+
if (!this.authToken) {
|
|
212
|
+
return { termsAccepted: false, privacyAccepted: false };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
try {
|
|
216
|
+
const result = await this.apiRequest('/api/users/compliance');
|
|
217
|
+
return result.compliance;
|
|
218
|
+
} catch (error) {
|
|
219
|
+
console.warn('UserDatabase: checkCompliance failed:', error.message);
|
|
220
|
+
return { termsAccepted: false, privacyAccepted: false };
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Set admin status (not allowed from client)
|
|
226
|
+
*/
|
|
227
|
+
async setAdminStatus(userId, isAdmin) {
|
|
228
|
+
throw new Error('setAdminStatus not allowed from client');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Check if user is an admin (not implemented client-side for security)
|
|
233
|
+
*/
|
|
234
|
+
async isAdmin(userId) {
|
|
235
|
+
return false; // Client-side can't check admin status
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Helper methods (same as server-side)
|
|
239
|
+
generateUserId(email) {
|
|
240
|
+
return crypto.createHash('sha256').update(email.toLowerCase()).digest('hex').substring(0, 16);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
generateComputerId() {
|
|
244
|
+
const hostname = os.hostname();
|
|
245
|
+
const platform = os.platform();
|
|
246
|
+
return crypto.createHash('sha256').update(`${hostname}-${platform}-${Date.now()}`).digest('hex').substring(0, 16);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
generateProjectId() {
|
|
250
|
+
return crypto.createHash('sha256').update(`${Date.now()}-${Math.random()}`).digest('hex').substring(0, 16);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
getLocalIP() {
|
|
254
|
+
const interfaces = os.networkInterfaces();
|
|
255
|
+
for (const name of Object.keys(interfaces)) {
|
|
256
|
+
for (const networkInterface of interfaces[name]) {
|
|
257
|
+
if (networkInterface.family === 'IPv4' && !networkInterface.internal) {
|
|
258
|
+
return networkInterface.address;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return '127.0.0.1';
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
module.exports = UserDatabaseClient;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User Database for VibeCodingMachine Client Applications
|
|
3
|
+
*
|
|
4
|
+
* This is the CLIENT-SIDE version that makes HTTP requests to the API server.
|
|
5
|
+
* The API server handles all direct AWS operations.
|
|
6
|
+
*
|
|
7
|
+
* This replaces the old direct AWS approach for better security and architecture.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const UserDatabaseClient = require('./user-database-client')
|
|
11
|
+
|
|
12
|
+
class UserDatabase {
|
|
13
|
+
constructor() {
|
|
14
|
+
// This is now ALWAYS the client version
|
|
15
|
+
// The API server uses a separate UserDatabase class
|
|
16
|
+
this.apiClient = new UserDatabaseClient()
|
|
17
|
+
console.log('UserDatabase: Client mode - will make API requests to server')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Set authentication token for API requests
|
|
22
|
+
*/
|
|
23
|
+
setAuthToken(token) {
|
|
24
|
+
this.apiClient.setAuthToken(token)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Register or update user in the database
|
|
29
|
+
*/
|
|
30
|
+
async registerUser(userInfo) {
|
|
31
|
+
return this.apiClient.registerUser(userInfo)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Register or update computer information
|
|
36
|
+
*/
|
|
37
|
+
async registerComputer(userId, computerInfo = {}) {
|
|
38
|
+
return this.apiClient.registerComputer(userId, computerInfo)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Register a new project
|
|
43
|
+
*/
|
|
44
|
+
async registerProject(userId, projectInfo) {
|
|
45
|
+
return this.apiClient.registerProject(userId, projectInfo)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Update user's terms and privacy acceptance
|
|
50
|
+
*/
|
|
51
|
+
async updateComplianceStatus(userId, complianceData) {
|
|
52
|
+
return this.apiClient.updateComplianceStatus(userId, complianceData)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Track user activity and interface usage
|
|
57
|
+
*/
|
|
58
|
+
async trackActivity(userId, activityData) {
|
|
59
|
+
return this.apiClient.trackActivity(userId, activityData)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get user by ID
|
|
64
|
+
*/
|
|
65
|
+
async getUser(userId) {
|
|
66
|
+
return this.apiClient.getUser(userId)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get user by email
|
|
71
|
+
*/
|
|
72
|
+
async getUserByEmail(email) {
|
|
73
|
+
return this.apiClient.getUserByEmail(email)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Update user activity timestamp
|
|
78
|
+
*/
|
|
79
|
+
async updateUserActivity(userId, updates) {
|
|
80
|
+
return this.apiClient.updateUserActivity(userId, updates)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Check if user has accepted terms and privacy policy
|
|
85
|
+
*/
|
|
86
|
+
async checkCompliance(userId) {
|
|
87
|
+
return this.apiClient.checkCompliance(userId)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Set admin status for a user (not allowed from client)
|
|
92
|
+
*/
|
|
93
|
+
async setAdminStatus(userId, isAdmin) {
|
|
94
|
+
return this.apiClient.setAdminStatus(userId, isAdmin)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Check if user is an admin
|
|
99
|
+
*/
|
|
100
|
+
async isAdmin(userId) {
|
|
101
|
+
return this.apiClient.isAdmin(userId)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Helper methods - delegate to API client
|
|
105
|
+
generateUserId(email) {
|
|
106
|
+
return this.apiClient.generateUserId(email)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
generateComputerId() {
|
|
110
|
+
return this.apiClient.generateComputerId()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
generateProjectId() {
|
|
114
|
+
return this.apiClient.generateProjectId()
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
module.exports = UserDatabase
|