@tradejs/app 1.0.5 → 1.0.8
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/bin/tradejs-app.mjs +129 -20
- package/package.json +13 -12
- package/public/auth-bg.jpg +0 -0
- package/public/next.svg +1 -0
- package/public/og-image-source.svg +91 -0
- package/public/og-image.png +0 -0
- package/public/vercel.svg +1 -0
- package/src/app/api/ai/route.ts +84 -20
- package/src/app/api/backtest/files/route.ts +12 -1
- package/src/app/api/backtest/test/[strategy]/[name]/route.ts +18 -1
- package/src/app/api/kline/[provider]/[symbol]/[interval]/route.ts +357 -29
- package/src/app/api/scanner/[provider]/route.ts +7 -1
- package/src/app/api/scanner/route.ts +7 -1
- package/src/app/api/signal/[symbol]/[signalId]/route.ts +6 -0
- package/src/app/api/user/settings/route.ts +244 -0
- package/src/app/components/Dashboard/AiDrawer/index.tsx +38 -51
- package/src/app/components/Shared/Filters/Backtest/index.tsx +12 -5
- package/src/app/components/Shared/Filters/Root/index.tsx +12 -1
- package/src/app/components/Shared/Filters/Symbol/index.tsx +14 -19
- package/src/app/components/Shared/Filters/context.ts +2 -0
- package/src/app/components/Shared/Sidebar/AccountSettingsDrawer.tsx +948 -0
- package/src/app/components/Shared/Sidebar/index.tsx +13 -9
- package/src/app/components/UI/ColorMode/index.tsx +62 -15
- package/src/app/components/UI/Select/index.tsx +3 -0
- package/src/app/components/UI/SelectWithSearch/index.tsx +3 -0
- package/src/app/globals.css +11 -0
- package/src/app/layout.tsx +50 -11
- package/src/app/lib/currentUser.ts +27 -0
- package/src/app/lib/klineWindow.ts +17 -0
- package/src/app/routes/dashboard/[provider]/[symbol]/[interval]/page.tsx +10 -2
- package/src/app/routes/signin/page.tsx +11 -2
- package/src/app/store/ai.ts +174 -0
- package/src/app/store/data.ts +219 -88
- package/src/app/store/index.ts +1 -0
- package/src/app/store/tests.ts +96 -9
- package/src/app/store/tickers.ts +113 -17
- package/src/proxy.ts +23 -50
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { intervalToMs, mergeData } from '@tradejs/core/data';
|
|
2
3
|
import {
|
|
3
4
|
createIndicators,
|
|
4
5
|
getRegisteredIndicatorEntries,
|
|
@@ -12,10 +13,15 @@ import {
|
|
|
12
13
|
Interval,
|
|
13
14
|
ConnectorCreator,
|
|
14
15
|
} from '@tradejs/types';
|
|
16
|
+
import { getCurrentUserName } from '@app/lib/currentUser';
|
|
17
|
+
import { normalizeEndToIntervalBoundary } from '@app/lib/klineWindow';
|
|
15
18
|
|
|
16
19
|
export const dynamic = 'force-dynamic';
|
|
17
20
|
const projectRoot =
|
|
18
21
|
String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
|
|
22
|
+
const DEFAULT_KLINE_CACHE_TTL_MS = 30_000;
|
|
23
|
+
const MAX_KLINE_CACHE_ENTRIES = 500;
|
|
24
|
+
const MAX_KLINE_CACHE_BYTES = 32 * 1024 * 1024;
|
|
19
25
|
|
|
20
26
|
interface Params {
|
|
21
27
|
provider: string;
|
|
@@ -23,6 +29,193 @@ interface Params {
|
|
|
23
29
|
interval: string;
|
|
24
30
|
}
|
|
25
31
|
|
|
32
|
+
type KlineCacheEntry = {
|
|
33
|
+
expiresAt: number;
|
|
34
|
+
sizeBytes: number;
|
|
35
|
+
value: KlineChartData;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
type ManagedKlineCache = {
|
|
39
|
+
entries: Map<string, KlineCacheEntry>;
|
|
40
|
+
totalBytes: number;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
type PluginRegistrySnapshot = {
|
|
44
|
+
pluginKeys: string[];
|
|
45
|
+
signature: string;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
declare global {
|
|
49
|
+
// eslint-disable-next-line no-var
|
|
50
|
+
var __tradejsKlineRawCache__: ManagedKlineCache | undefined;
|
|
51
|
+
// eslint-disable-next-line no-var
|
|
52
|
+
var __tradejsKlineBtcRawCache__: ManagedKlineCache | undefined;
|
|
53
|
+
// eslint-disable-next-line no-var
|
|
54
|
+
var __tradejsKlineEnrichedCache__: ManagedKlineCache | undefined;
|
|
55
|
+
// eslint-disable-next-line no-var
|
|
56
|
+
var __tradejsKlineInflightRequests__:
|
|
57
|
+
| Map<string, Promise<KlineChartData>>
|
|
58
|
+
| undefined;
|
|
59
|
+
// eslint-disable-next-line no-var
|
|
60
|
+
var __tradejsPluginRegistrySnapshotPromise__:
|
|
61
|
+
| Promise<PluginRegistrySnapshot>
|
|
62
|
+
| undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const cloneKlineData = (data: KlineChartData) =>
|
|
66
|
+
data.map((candle) => ({ ...candle })) as KlineChartData;
|
|
67
|
+
|
|
68
|
+
const createManagedCache = (): ManagedKlineCache => ({
|
|
69
|
+
entries: new Map<string, KlineCacheEntry>(),
|
|
70
|
+
totalBytes: 0,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const getRawKlineCache = () => {
|
|
74
|
+
if (!global.__tradejsKlineRawCache__) {
|
|
75
|
+
global.__tradejsKlineRawCache__ = createManagedCache();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return global.__tradejsKlineRawCache__;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const getBtcKlineCache = () => {
|
|
82
|
+
if (!global.__tradejsKlineBtcRawCache__) {
|
|
83
|
+
global.__tradejsKlineBtcRawCache__ = createManagedCache();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return global.__tradejsKlineBtcRawCache__;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const getEnrichedKlineCache = () => {
|
|
90
|
+
if (!global.__tradejsKlineEnrichedCache__) {
|
|
91
|
+
global.__tradejsKlineEnrichedCache__ = createManagedCache();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return global.__tradejsKlineEnrichedCache__;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const getInflightRequestMap = () => {
|
|
98
|
+
if (!global.__tradejsKlineInflightRequests__) {
|
|
99
|
+
global.__tradejsKlineInflightRequests__ = new Map<
|
|
100
|
+
string,
|
|
101
|
+
Promise<KlineChartData>
|
|
102
|
+
>();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return global.__tradejsKlineInflightRequests__;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const measureKlineDataBytes = (value: KlineChartData) =>
|
|
109
|
+
Buffer.byteLength(JSON.stringify(value), 'utf8');
|
|
110
|
+
|
|
111
|
+
const pruneCache = (cache: ManagedKlineCache, now: number) => {
|
|
112
|
+
for (const [key, entry] of cache.entries) {
|
|
113
|
+
if (entry.expiresAt <= now) {
|
|
114
|
+
cache.entries.delete(key);
|
|
115
|
+
cache.totalBytes -= entry.sizeBytes;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
while (
|
|
120
|
+
cache.entries.size > MAX_KLINE_CACHE_ENTRIES ||
|
|
121
|
+
cache.totalBytes > MAX_KLINE_CACHE_BYTES
|
|
122
|
+
) {
|
|
123
|
+
const oldestKey = cache.entries.keys().next().value;
|
|
124
|
+
if (!oldestKey) {
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const entry = cache.entries.get(oldestKey);
|
|
129
|
+
cache.entries.delete(oldestKey);
|
|
130
|
+
cache.totalBytes -= entry?.sizeBytes ?? 0;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
cache.totalBytes = Math.max(0, cache.totalBytes);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const getCacheTtlMs = (interval: Interval) =>
|
|
137
|
+
Math.min(intervalToMs(interval), DEFAULT_KLINE_CACHE_TTL_MS);
|
|
138
|
+
|
|
139
|
+
const buildRawCacheKey = (params: {
|
|
140
|
+
provider: string;
|
|
141
|
+
symbol: string;
|
|
142
|
+
interval: Interval;
|
|
143
|
+
start: number;
|
|
144
|
+
end: number;
|
|
145
|
+
cacheOnly?: boolean;
|
|
146
|
+
}) =>
|
|
147
|
+
[
|
|
148
|
+
params.provider,
|
|
149
|
+
params.symbol,
|
|
150
|
+
params.interval,
|
|
151
|
+
params.start,
|
|
152
|
+
params.end,
|
|
153
|
+
params.cacheOnly ? 'cache' : 'live',
|
|
154
|
+
].join(':');
|
|
155
|
+
|
|
156
|
+
const buildEnrichedCacheKey = (params: {
|
|
157
|
+
userName: string;
|
|
158
|
+
provider: string;
|
|
159
|
+
symbol: string;
|
|
160
|
+
interval: Interval;
|
|
161
|
+
start: number;
|
|
162
|
+
end: number;
|
|
163
|
+
historicalEnd: number;
|
|
164
|
+
cacheOnly?: boolean;
|
|
165
|
+
pluginSignature: string;
|
|
166
|
+
}) =>
|
|
167
|
+
[
|
|
168
|
+
params.userName,
|
|
169
|
+
params.provider,
|
|
170
|
+
params.symbol,
|
|
171
|
+
params.interval,
|
|
172
|
+
params.start,
|
|
173
|
+
params.end,
|
|
174
|
+
params.historicalEnd,
|
|
175
|
+
params.cacheOnly ? 'cache' : 'live',
|
|
176
|
+
params.pluginSignature,
|
|
177
|
+
].join(':');
|
|
178
|
+
|
|
179
|
+
const getCachedKline = (cache: ManagedKlineCache, key: string, now: number) => {
|
|
180
|
+
const cached = cache.entries.get(key);
|
|
181
|
+
if (!cached) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (cached.expiresAt <= now) {
|
|
186
|
+
cache.entries.delete(key);
|
|
187
|
+
cache.totalBytes -= cached.sizeBytes;
|
|
188
|
+
cache.totalBytes = Math.max(0, cache.totalBytes);
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return cloneKlineData(cached.value);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const setCachedKline = (
|
|
196
|
+
cache: ManagedKlineCache,
|
|
197
|
+
key: string,
|
|
198
|
+
value: KlineChartData,
|
|
199
|
+
ttlMs: number,
|
|
200
|
+
now: number,
|
|
201
|
+
) => {
|
|
202
|
+
const nextValue = cloneKlineData(value);
|
|
203
|
+
const nextSizeBytes = measureKlineDataBytes(nextValue);
|
|
204
|
+
const previous = cache.entries.get(key);
|
|
205
|
+
|
|
206
|
+
if (previous) {
|
|
207
|
+
cache.totalBytes -= previous.sizeBytes;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
cache.entries.set(key, {
|
|
211
|
+
expiresAt: now + ttlMs,
|
|
212
|
+
sizeBytes: nextSizeBytes,
|
|
213
|
+
value: nextValue,
|
|
214
|
+
});
|
|
215
|
+
cache.totalBytes += nextSizeBytes;
|
|
216
|
+
pruneCache(cache, now);
|
|
217
|
+
};
|
|
218
|
+
|
|
26
219
|
const enrichWithPluginIndicators = (
|
|
27
220
|
data: KlineChartData,
|
|
28
221
|
btcData: KlineChartData,
|
|
@@ -37,7 +230,7 @@ const enrichWithPluginIndicators = (
|
|
|
37
230
|
pluginRegistryScope: projectRoot,
|
|
38
231
|
}).result() as Record<string, number[]>;
|
|
39
232
|
|
|
40
|
-
const nextData = data
|
|
233
|
+
const nextData = cloneKlineData(data);
|
|
41
234
|
|
|
42
235
|
for (const pluginKey of pluginKeys) {
|
|
43
236
|
const series = history[pluginKey];
|
|
@@ -62,11 +255,37 @@ const enrichWithPluginIndicators = (
|
|
|
62
255
|
return nextData;
|
|
63
256
|
};
|
|
64
257
|
|
|
258
|
+
const getPluginRegistrySnapshot = async (): Promise<PluginRegistrySnapshot> => {
|
|
259
|
+
if (!global.__tradejsPluginRegistrySnapshotPromise__) {
|
|
260
|
+
global.__tradejsPluginRegistrySnapshotPromise__ = (async () => {
|
|
261
|
+
await ensureIndicatorPluginsLoaded(projectRoot);
|
|
262
|
+
const pluginKeys = getRegisteredIndicatorEntries(projectRoot)
|
|
263
|
+
.map((entry) => entry.historyKey || entry.indicator.id)
|
|
264
|
+
.sort();
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
pluginKeys,
|
|
268
|
+
signature: pluginKeys.join(','),
|
|
269
|
+
};
|
|
270
|
+
})().catch((error) => {
|
|
271
|
+
global.__tradejsPluginRegistrySnapshotPromise__ = undefined;
|
|
272
|
+
throw error;
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return global.__tradejsPluginRegistrySnapshotPromise__;
|
|
277
|
+
};
|
|
278
|
+
|
|
65
279
|
export const POST = async (
|
|
66
280
|
request: NextRequest,
|
|
67
281
|
{ params }: { params: Promise<Params> },
|
|
68
282
|
) => {
|
|
69
283
|
try {
|
|
284
|
+
const userName = await getCurrentUserName();
|
|
285
|
+
if (!userName) {
|
|
286
|
+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
287
|
+
}
|
|
288
|
+
|
|
70
289
|
const { provider, symbol, interval } = await params;
|
|
71
290
|
const body = await request.json();
|
|
72
291
|
const options = body as
|
|
@@ -80,42 +299,151 @@ export const POST = async (
|
|
|
80
299
|
);
|
|
81
300
|
}
|
|
82
301
|
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const baseData = await connector.kline({
|
|
302
|
+
const typedInterval = interval as Interval;
|
|
303
|
+
const normalizedEnd = normalizeEndToIntervalBoundary(
|
|
304
|
+
Number(options.end),
|
|
305
|
+
typedInterval,
|
|
306
|
+
);
|
|
307
|
+
const historicalEnd = Math.min(Number(options.end), normalizedEnd);
|
|
308
|
+
const pluginSnapshot = await getPluginRegistrySnapshot();
|
|
309
|
+
const requestKey = buildEnrichedCacheKey({
|
|
310
|
+
userName,
|
|
311
|
+
provider,
|
|
94
312
|
symbol,
|
|
95
|
-
interval:
|
|
96
|
-
|
|
313
|
+
interval: typedInterval,
|
|
314
|
+
start: Number(options.start ?? 0),
|
|
315
|
+
end: Number(options.end),
|
|
316
|
+
historicalEnd,
|
|
317
|
+
cacheOnly: Boolean(options.cacheOnly),
|
|
318
|
+
pluginSignature: pluginSnapshot.signature,
|
|
97
319
|
});
|
|
320
|
+
const now = Date.now();
|
|
321
|
+
const ttlMs = getCacheTtlMs(typedInterval);
|
|
322
|
+
const enrichedCache = getEnrichedKlineCache();
|
|
323
|
+
const cachedEnriched = getCachedKline(enrichedCache, requestKey, now);
|
|
324
|
+
if (cachedEnriched) {
|
|
325
|
+
return NextResponse.json({ data: cachedEnriched });
|
|
326
|
+
}
|
|
98
327
|
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (!pluginKeys.length) {
|
|
104
|
-
return NextResponse.json({ data: baseData });
|
|
328
|
+
const inflightRequests = getInflightRequestMap();
|
|
329
|
+
const inflight = inflightRequests.get(requestKey);
|
|
330
|
+
if (inflight) {
|
|
331
|
+
return NextResponse.json({ data: await inflight });
|
|
105
332
|
}
|
|
106
333
|
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
334
|
+
const pending = (async () => {
|
|
335
|
+
const connectorCreator =
|
|
336
|
+
(await getConnectorCreatorByProvider(provider, projectRoot)) ||
|
|
337
|
+
(await getConnectorCreatorByProvider('bybit', projectRoot));
|
|
338
|
+
if (!connectorCreator) {
|
|
339
|
+
throw new Error('No connector available for provider');
|
|
340
|
+
}
|
|
341
|
+
const connector = await (connectorCreator as ConnectorCreator)({
|
|
342
|
+
userName,
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
const liveTailRequired = Number(options.end) > historicalEnd;
|
|
346
|
+
const liveTailStart = Math.max(Number(options.start ?? 0), historicalEnd);
|
|
347
|
+
const rawCache = getRawKlineCache();
|
|
348
|
+
const btcCache = getBtcKlineCache();
|
|
349
|
+
|
|
350
|
+
const fetchRawSegment = async (segmentParams: {
|
|
351
|
+
symbol: string;
|
|
352
|
+
start: number;
|
|
353
|
+
end: number;
|
|
354
|
+
useBtcCache?: boolean;
|
|
355
|
+
}) => {
|
|
356
|
+
if (segmentParams.end <= segmentParams.start) {
|
|
357
|
+
return [] as KlineChartData;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const cache = segmentParams.useBtcCache ? btcCache : rawCache;
|
|
361
|
+
const cacheKey = buildRawCacheKey({
|
|
362
|
+
provider,
|
|
363
|
+
symbol: segmentParams.symbol,
|
|
364
|
+
interval: typedInterval,
|
|
365
|
+
start: segmentParams.start,
|
|
366
|
+
end: segmentParams.end,
|
|
367
|
+
cacheOnly: Boolean(options.cacheOnly),
|
|
368
|
+
});
|
|
369
|
+
const cached = getCachedKline(cache, cacheKey, now);
|
|
370
|
+
if (cached) {
|
|
371
|
+
return cached;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const data = await connector.kline({
|
|
375
|
+
symbol: segmentParams.symbol,
|
|
376
|
+
interval: typedInterval,
|
|
377
|
+
...options,
|
|
378
|
+
start: segmentParams.start,
|
|
379
|
+
end: segmentParams.end,
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
setCachedKline(cache, cacheKey, data, ttlMs, now);
|
|
383
|
+
return data;
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
const baseHistorical =
|
|
387
|
+
historicalEnd > Number(options.start ?? 0)
|
|
388
|
+
? await fetchRawSegment({
|
|
389
|
+
symbol,
|
|
390
|
+
start: Number(options.start ?? 0),
|
|
391
|
+
end: historicalEnd,
|
|
392
|
+
})
|
|
393
|
+
: [];
|
|
394
|
+
const baseLiveTail = liveTailRequired
|
|
395
|
+
? await connector.kline({
|
|
396
|
+
symbol,
|
|
397
|
+
interval: typedInterval,
|
|
113
398
|
...options,
|
|
114
|
-
|
|
399
|
+
start: liveTailStart,
|
|
400
|
+
end: Number(options.end),
|
|
401
|
+
})
|
|
402
|
+
: [];
|
|
403
|
+
const baseData = mergeData(baseHistorical, baseLiveTail);
|
|
404
|
+
|
|
405
|
+
if (!pluginSnapshot.pluginKeys.length) {
|
|
406
|
+
setCachedKline(enrichedCache, requestKey, baseData, ttlMs, now);
|
|
407
|
+
return baseData;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const btcData =
|
|
411
|
+
symbol === 'BTCUSDT'
|
|
412
|
+
? baseData
|
|
413
|
+
: mergeData(
|
|
414
|
+
historicalEnd > Number(options.start ?? 0)
|
|
415
|
+
? await fetchRawSegment({
|
|
416
|
+
symbol: 'BTCUSDT',
|
|
417
|
+
start: Number(options.start ?? 0),
|
|
418
|
+
end: historicalEnd,
|
|
419
|
+
useBtcCache: true,
|
|
420
|
+
})
|
|
421
|
+
: [],
|
|
422
|
+
liveTailRequired
|
|
423
|
+
? await connector.kline({
|
|
424
|
+
symbol: 'BTCUSDT',
|
|
425
|
+
interval: typedInterval,
|
|
426
|
+
...options,
|
|
427
|
+
start: liveTailStart,
|
|
428
|
+
end: Number(options.end),
|
|
429
|
+
})
|
|
430
|
+
: [],
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
const data = enrichWithPluginIndicators(
|
|
434
|
+
baseData,
|
|
435
|
+
btcData,
|
|
436
|
+
pluginSnapshot.pluginKeys,
|
|
437
|
+
);
|
|
438
|
+
setCachedKline(enrichedCache, requestKey, data, ttlMs, now);
|
|
439
|
+
return data;
|
|
440
|
+
})().finally(() => {
|
|
441
|
+
inflightRequests.delete(requestKey);
|
|
442
|
+
});
|
|
115
443
|
|
|
116
|
-
|
|
444
|
+
inflightRequests.set(requestKey, pending);
|
|
117
445
|
|
|
118
|
-
return NextResponse.json({ data });
|
|
446
|
+
return NextResponse.json({ data: await pending });
|
|
119
447
|
} catch (error) {
|
|
120
448
|
logger.log('error', `Kline fetch error: %o`, error);
|
|
121
449
|
return NextResponse.json(
|
|
@@ -3,6 +3,7 @@ import { ConnectorCreator } from '@tradejs/types';
|
|
|
3
3
|
import { getConnectorCreatorByProvider } from '@tradejs/node/connectors';
|
|
4
4
|
import { getTopTickers } from '@tradejs/core/tickers';
|
|
5
5
|
import { logger } from '@tradejs/infra/logger';
|
|
6
|
+
import { getCurrentUserName } from '@app/lib/currentUser';
|
|
6
7
|
|
|
7
8
|
export const dynamic = 'force-dynamic';
|
|
8
9
|
const projectRoot =
|
|
@@ -17,6 +18,11 @@ export const GET = async (
|
|
|
17
18
|
{ params }: { params: Promise<Params> },
|
|
18
19
|
) => {
|
|
19
20
|
try {
|
|
21
|
+
const userName = await getCurrentUserName();
|
|
22
|
+
if (!userName) {
|
|
23
|
+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
24
|
+
}
|
|
25
|
+
|
|
20
26
|
const { provider } = await params;
|
|
21
27
|
const connectorCreator =
|
|
22
28
|
(await getConnectorCreatorByProvider(provider, projectRoot)) ||
|
|
@@ -26,7 +32,7 @@ export const GET = async (
|
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
const connector = await (connectorCreator as ConnectorCreator)({
|
|
29
|
-
userName
|
|
35
|
+
userName,
|
|
30
36
|
});
|
|
31
37
|
|
|
32
38
|
const data = await connector.getTickers();
|
|
@@ -3,6 +3,7 @@ import { getTopTickers } from '@tradejs/core/tickers';
|
|
|
3
3
|
import { getConnectorCreatorByProvider } from '@tradejs/node/connectors';
|
|
4
4
|
import { logger } from '@tradejs/infra/logger';
|
|
5
5
|
import { ConnectorCreator } from '@tradejs/types';
|
|
6
|
+
import { getCurrentUserName } from '@app/lib/currentUser';
|
|
6
7
|
|
|
7
8
|
export const dynamic = 'force-dynamic';
|
|
8
9
|
const projectRoot =
|
|
@@ -10,6 +11,11 @@ const projectRoot =
|
|
|
10
11
|
|
|
11
12
|
export const GET = async () => {
|
|
12
13
|
try {
|
|
14
|
+
const userName = await getCurrentUserName();
|
|
15
|
+
if (!userName) {
|
|
16
|
+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
17
|
+
}
|
|
18
|
+
|
|
13
19
|
const connectorCreator = await getConnectorCreatorByProvider(
|
|
14
20
|
'bybit',
|
|
15
21
|
projectRoot,
|
|
@@ -19,7 +25,7 @@ export const GET = async () => {
|
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
const byBitConnector = await (connectorCreator as ConnectorCreator)({
|
|
22
|
-
userName
|
|
28
|
+
userName,
|
|
23
29
|
});
|
|
24
30
|
|
|
25
31
|
const data = await byBitConnector.getTickers();
|
|
@@ -3,6 +3,7 @@ import { NextResponse } from 'next/server';
|
|
|
3
3
|
import { getData, redisKeys } from '@tradejs/infra/redis';
|
|
4
4
|
import { logger } from '@tradejs/infra/logger';
|
|
5
5
|
import { Signal } from '@tradejs/types';
|
|
6
|
+
import { getCurrentUserName } from '@app/lib/currentUser';
|
|
6
7
|
|
|
7
8
|
export const dynamic = 'force-dynamic';
|
|
8
9
|
|
|
@@ -16,6 +17,11 @@ export const GET = async (
|
|
|
16
17
|
{ params }: { params: Promise<Params> },
|
|
17
18
|
) => {
|
|
18
19
|
try {
|
|
20
|
+
const userName = await getCurrentUserName();
|
|
21
|
+
if (!userName) {
|
|
22
|
+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
const { symbol, signalId } = await params;
|
|
20
26
|
|
|
21
27
|
if (!symbol || !signalId) {
|