upfynai-code 2.5.1 → 2.6.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/package.json +2 -8
- package/server/cli.js +1 -1
- package/server/database/db.js +16 -2
- package/server/index.js +2738 -2621
- package/server/middleware/auth.js +10 -2
- package/server/relay-client.js +73 -20
- package/server/routes/agent.js +1226 -1266
- package/server/routes/auth.js +32 -29
- package/server/routes/commands.js +598 -601
- package/server/routes/cursor.js +806 -807
- package/server/routes/dashboard.js +154 -1
- package/server/routes/git.js +1151 -1165
- package/server/routes/mcp.js +534 -551
- package/server/routes/settings.js +261 -269
- package/server/routes/taskmaster.js +1927 -1963
- package/server/routes/vapi-chat.js +94 -0
- package/server/routes/voice.js +0 -4
- package/server/sandbox.js +120 -0
|
@@ -1,10 +1,163 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
2
|
import { getProjects, getSessions } from '../projects.js';
|
|
3
|
+
import { userDb, subscriptionDb, paymentDb, apiKeysDb, credentialsDb, relayTokensDb, webhookDb, workflowDb } from '../database/db.js';
|
|
3
4
|
|
|
4
5
|
const router = Router();
|
|
5
6
|
|
|
7
|
+
// AI provider credential types
|
|
8
|
+
const AI_PROVIDER_TYPES = ['anthropic_key', 'openai_key', 'openrouter_key', 'google_key'];
|
|
9
|
+
const PROVIDER_LABELS = {
|
|
10
|
+
anthropic_key: 'Anthropic',
|
|
11
|
+
openai_key: 'OpenAI',
|
|
12
|
+
openrouter_key: 'OpenRouter',
|
|
13
|
+
google_key: 'Google',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* GET /api/dashboard/user-stats — Per-user dashboard data from Turso
|
|
18
|
+
* Aggregates: account, subscription, payments, API keys, AI providers, relay tokens, webhooks, workflows
|
|
19
|
+
*/
|
|
20
|
+
router.get('/user-stats', async (req, res) => {
|
|
21
|
+
try {
|
|
22
|
+
const userId = req.user.id;
|
|
23
|
+
|
|
24
|
+
// Run all DB queries in parallel
|
|
25
|
+
const [
|
|
26
|
+
fullUser,
|
|
27
|
+
activeSub,
|
|
28
|
+
allSubs,
|
|
29
|
+
payments,
|
|
30
|
+
apiKeys,
|
|
31
|
+
credentials,
|
|
32
|
+
relayTokens,
|
|
33
|
+
webhooks,
|
|
34
|
+
workflows,
|
|
35
|
+
] = await Promise.all([
|
|
36
|
+
userDb.getUserById(userId),
|
|
37
|
+
subscriptionDb.getActiveSub(userId).catch(() => null),
|
|
38
|
+
subscriptionDb.getAllSubs(userId).catch(() => []),
|
|
39
|
+
paymentDb.getUserPayments(userId).catch(() => []),
|
|
40
|
+
apiKeysDb.getApiKeys(userId).catch(() => []),
|
|
41
|
+
credentialsDb.getCredentials(userId).catch(() => []),
|
|
42
|
+
relayTokensDb.getTokens(userId).catch(() => []),
|
|
43
|
+
webhookDb.getAll(userId).catch(() => []),
|
|
44
|
+
workflowDb.getAll(userId).catch(() => []),
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
// Expire overdue subs
|
|
48
|
+
try { await subscriptionDb.expireOverdue(); } catch { /* non-critical */ }
|
|
49
|
+
|
|
50
|
+
// Account
|
|
51
|
+
const account = fullUser ? {
|
|
52
|
+
username: fullUser.username,
|
|
53
|
+
email: fullUser.email,
|
|
54
|
+
phone: fullUser.phone,
|
|
55
|
+
firstName: fullUser.first_name,
|
|
56
|
+
lastName: fullUser.last_name,
|
|
57
|
+
userCode: fullUser.user_code,
|
|
58
|
+
createdAt: fullUser.created_at,
|
|
59
|
+
lastLogin: fullUser.last_login,
|
|
60
|
+
accessOverride: fullUser.access_override,
|
|
61
|
+
} : null;
|
|
62
|
+
|
|
63
|
+
// Subscription
|
|
64
|
+
const subscription = {
|
|
65
|
+
active: activeSub ? {
|
|
66
|
+
id: activeSub.id,
|
|
67
|
+
planId: activeSub.plan_id,
|
|
68
|
+
status: activeSub.status,
|
|
69
|
+
startsAt: activeSub.starts_at,
|
|
70
|
+
expiresAt: activeSub.expires_at,
|
|
71
|
+
amount: activeSub.amount,
|
|
72
|
+
currency: activeSub.currency || 'INR',
|
|
73
|
+
} : null,
|
|
74
|
+
history: allSubs.map(s => ({
|
|
75
|
+
id: s.id,
|
|
76
|
+
planId: s.plan_id,
|
|
77
|
+
status: s.status,
|
|
78
|
+
startsAt: s.starts_at,
|
|
79
|
+
expiresAt: s.expires_at,
|
|
80
|
+
amount: s.amount,
|
|
81
|
+
currency: s.currency || 'INR',
|
|
82
|
+
createdAt: s.created_at,
|
|
83
|
+
})),
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// Payments
|
|
87
|
+
const paidPayments = payments.filter(p => p.status === 'paid');
|
|
88
|
+
const totalAmountPaise = paidPayments.reduce((sum, p) => sum + (Number(p.amount) || 0), 0);
|
|
89
|
+
const paymentData = {
|
|
90
|
+
total: payments.length,
|
|
91
|
+
paid: paidPayments.length,
|
|
92
|
+
totalAmountPaise,
|
|
93
|
+
recent: payments.slice(0, 10).map(p => ({
|
|
94
|
+
id: p.id,
|
|
95
|
+
planId: p.plan_id,
|
|
96
|
+
amount: p.amount,
|
|
97
|
+
currency: p.currency || 'INR',
|
|
98
|
+
status: p.status,
|
|
99
|
+
createdAt: p.created_at,
|
|
100
|
+
})),
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// API Keys
|
|
104
|
+
const activeKeys = apiKeys.filter(k => k.is_active);
|
|
105
|
+
const apiKeyData = {
|
|
106
|
+
total: apiKeys.length,
|
|
107
|
+
active: activeKeys.length,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// AI Providers (filter credentials by AI provider types)
|
|
111
|
+
const aiCreds = credentials.filter(c => AI_PROVIDER_TYPES.includes(c.credential_type));
|
|
112
|
+
const activeAiCreds = aiCreds.filter(c => c.is_active);
|
|
113
|
+
const aiProviderData = {
|
|
114
|
+
total: aiCreds.length,
|
|
115
|
+
active: activeAiCreds.length,
|
|
116
|
+
providers: activeAiCreds.map(c => PROVIDER_LABELS[c.credential_type] || c.credential_type),
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// Relay Tokens
|
|
120
|
+
const activeTokens = relayTokens.filter(t => t.is_active);
|
|
121
|
+
const lastConnected = relayTokens
|
|
122
|
+
.filter(t => t.last_connected)
|
|
123
|
+
.sort((a, b) => new Date(b.last_connected) - new Date(a.last_connected))[0]?.last_connected || null;
|
|
124
|
+
const relayData = {
|
|
125
|
+
total: relayTokens.length,
|
|
126
|
+
active: activeTokens.length,
|
|
127
|
+
lastConnected,
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// Webhooks
|
|
131
|
+
const activeWebhooks = webhooks.filter(w => w.is_active);
|
|
132
|
+
const webhookData = {
|
|
133
|
+
total: webhooks.length,
|
|
134
|
+
active: activeWebhooks.length,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// Workflows
|
|
138
|
+
const activeWorkflows = workflows.filter(w => w.is_active);
|
|
139
|
+
const workflowData = {
|
|
140
|
+
total: workflows.length,
|
|
141
|
+
active: activeWorkflows.length,
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
res.json({
|
|
145
|
+
account,
|
|
146
|
+
subscription,
|
|
147
|
+
payments: paymentData,
|
|
148
|
+
apiKeys: apiKeyData,
|
|
149
|
+
aiProviders: aiProviderData,
|
|
150
|
+
relayTokens: relayData,
|
|
151
|
+
webhooks: webhookData,
|
|
152
|
+
workflows: workflowData,
|
|
153
|
+
});
|
|
154
|
+
} catch (error) {
|
|
155
|
+
res.status(500).json({ error: 'Failed to fetch user stats' });
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
6
159
|
/**
|
|
7
|
-
* GET /api/dashboard/stats — Dashboard usage analytics
|
|
160
|
+
* GET /api/dashboard/stats — Dashboard usage analytics (local projects)
|
|
8
161
|
* Returns session counts, provider breakdown, and today's activity.
|
|
9
162
|
*/
|
|
10
163
|
router.get('/stats', async (req, res) => {
|