vibecodingmachine-core 2025.12.6-1702 → 2025.12.24-2348

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.
@@ -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