@tradejs/app 1.0.5 → 1.0.6

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.
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { spawn } from 'child_process';
4
4
  import { createRequire } from 'module';
5
+ import net from 'net';
5
6
  import path from 'path';
6
7
  import { fileURLToPath } from 'url';
7
8
  import nextEnv from '@next/env';
@@ -11,6 +12,13 @@ const { loadEnvConfig } = nextEnv;
11
12
  const __filename = fileURLToPath(import.meta.url);
12
13
  const __dirname = path.dirname(__filename);
13
14
  const appDir = path.resolve(__dirname, '..');
15
+ const LOCAL_DEV_HOSTNAMES = new Set([
16
+ 'localhost',
17
+ '127.0.0.1',
18
+ '0.0.0.0',
19
+ '::1',
20
+ '[::1]',
21
+ ]);
14
22
 
15
23
  const command = process.argv[2] || 'dev';
16
24
  const rawArgs = process.argv.slice(3);
@@ -23,32 +31,126 @@ process.env.PROJECT_CWD = projectCwd;
23
31
  const dev = command === 'dev';
24
32
  loadEnvConfig(projectCwd, dev, console);
25
33
 
26
- const nextBin = require.resolve('next/dist/bin/next');
27
- const args = [nextBin, command, ...rawArgs];
34
+ function parsePort(value) {
35
+ const parsed = Number.parseInt(String(value || '').trim(), 10);
36
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : null;
37
+ }
38
+
39
+ function readArgValue(args, flags) {
40
+ for (let index = 0; index < args.length; index += 1) {
41
+ const arg = args[index];
42
+ if (flags.includes(arg)) {
43
+ return args[index + 1] || null;
44
+ }
45
+ const matchedFlag = flags.find((flag) => arg.startsWith(`${flag}=`));
46
+ if (matchedFlag) {
47
+ return arg.slice(matchedFlag.length + 1) || null;
48
+ }
49
+ }
28
50
 
29
- if (
30
- command === 'start' &&
31
- !rawArgs.includes('-H') &&
32
- !rawArgs.includes('--hostname')
33
- ) {
34
- args.push('-H', '0.0.0.0');
51
+ return null;
35
52
  }
36
53
 
37
- const child = spawn(process.execPath, args, {
38
- cwd: appDir,
39
- env: process.env,
40
- stdio: 'inherit',
41
- });
54
+ async function isPortAvailable(port) {
55
+ return new Promise((resolve) => {
56
+ const server = net.createServer();
57
+ server.unref();
58
+ server.once('error', () => resolve(false));
59
+ server.listen(port, () => {
60
+ server.close(() => resolve(true));
61
+ });
62
+ });
63
+ }
64
+
65
+ async function findAvailablePort(startPort, attempts = 20) {
66
+ for (let offset = 0; offset < attempts; offset += 1) {
67
+ const port = startPort + offset;
68
+ if (await isPortAvailable(port)) {
69
+ return port;
70
+ }
71
+ }
42
72
 
43
- child.on('exit', (code, signal) => {
44
- if (signal) {
45
- process.kill(process.pid, signal);
73
+ return null;
74
+ }
75
+
76
+ function syncLocalUrlEnv(name, fromPort, toPort) {
77
+ const rawValue = String(process.env[name] || '').trim();
78
+ if (!rawValue) {
79
+ process.env[name] = `http://localhost:${toPort}`;
46
80
  return;
47
81
  }
48
- process.exit(code ?? 0);
49
- });
50
82
 
51
- child.on('error', (error) => {
52
- console.error('[tradejs-app] failed to start Next.js:', error);
83
+ try {
84
+ const url = new URL(rawValue);
85
+ const currentPort =
86
+ parsePort(url.port) || (url.protocol === 'https:' ? 443 : 80);
87
+ if (!LOCAL_DEV_HOSTNAMES.has(url.hostname) || currentPort !== fromPort) {
88
+ return;
89
+ }
90
+ url.port = String(toPort);
91
+ process.env[name] = url.toString();
92
+ } catch {
93
+ // Ignore invalid URLs and leave user-provided values untouched.
94
+ }
95
+ }
96
+
97
+ async function main() {
98
+ const nextBin = require.resolve('next/dist/bin/next');
99
+ const args = [nextBin, command, ...rawArgs];
100
+ const explicitPort = parsePort(readArgValue(rawArgs, ['-p', '--port']));
101
+
102
+ if (dev && explicitPort === null) {
103
+ const requestedPort = parsePort(process.env.PORT) || 3000;
104
+ const resolvedPort = await findAvailablePort(requestedPort);
105
+
106
+ if (resolvedPort === null) {
107
+ console.error(
108
+ `[tradejs-app] no available dev port found starting at ${requestedPort}`,
109
+ );
110
+ process.exit(1);
111
+ }
112
+
113
+ process.env.PORT = String(resolvedPort);
114
+ args.push('-p', String(resolvedPort));
115
+
116
+ if (resolvedPort !== requestedPort) {
117
+ syncLocalUrlEnv('APP_URL', requestedPort, resolvedPort);
118
+ syncLocalUrlEnv('NEXTAUTH_URL', requestedPort, resolvedPort);
119
+ console.warn(
120
+ `[tradejs-app] port ${requestedPort} is busy, using ${resolvedPort} instead`,
121
+ );
122
+ }
123
+ }
124
+
125
+ if (
126
+ command === 'start' &&
127
+ !rawArgs.includes('-H') &&
128
+ !rawArgs.includes('--hostname')
129
+ ) {
130
+ args.push('-H', '0.0.0.0');
131
+ }
132
+
133
+ const child = spawn(process.execPath, args, {
134
+ cwd: appDir,
135
+ env: process.env,
136
+ stdio: 'inherit',
137
+ });
138
+
139
+ child.on('exit', (code, signal) => {
140
+ if (signal) {
141
+ process.kill(process.pid, signal);
142
+ return;
143
+ }
144
+ process.exit(code ?? 0);
145
+ });
146
+
147
+ child.on('error', (error) => {
148
+ console.error('[tradejs-app] failed to start Next.js:', error);
149
+ process.exit(1);
150
+ });
151
+ }
152
+
153
+ main().catch((error) => {
154
+ console.error('[tradejs-app] failed to initialize:', error);
53
155
  process.exit(1);
54
156
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradejs/app",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "Installable Next.js UI for the TradeJS open-source framework: dashboards, backtests, charts, and runtime data.",
5
5
  "keywords": [
6
6
  "tradejs",
@@ -14,6 +14,7 @@
14
14
  "files": [
15
15
  "README.md",
16
16
  "bin",
17
+ "public",
17
18
  "src",
18
19
  "!src/**/__tests__",
19
20
  "!src/**/*.test.ts",
@@ -42,18 +43,18 @@
42
43
  "@emotion/react": "^11.14.0",
43
44
  "@langchain/core": "^0.3.68",
44
45
  "@langchain/openai": "^0.6.11",
45
- "@tradejs/connectors": "^1.0.5",
46
- "@tradejs/core": "^1.0.5",
47
- "@tradejs/indicators": "^1.0.5",
48
- "@tradejs/infra": "^1.0.5",
49
- "@tradejs/node": "^1.0.5",
50
- "@tradejs/types": "^1.0.5",
46
+ "@tradejs/connectors": "^1.0.6",
47
+ "@tradejs/core": "^1.0.6",
48
+ "@tradejs/indicators": "^1.0.6",
49
+ "@tradejs/infra": "^1.0.6",
50
+ "@tradejs/node": "^1.0.6",
51
+ "@tradejs/types": "^1.0.6",
51
52
  "bcryptjs": "^2.4.3",
52
53
  "date-fns": "^3.3.1",
53
54
  "idb-keyval": "^6.2.2",
54
55
  "klinecharts": "10.0.0-alpha9",
55
56
  "lodash": "^4.17.21",
56
- "next": "^16.1.1",
57
+ "next": "^16.1.7",
57
58
  "next-auth": "^5.0.0-beta.26",
58
59
  "next-themes": "^0.4.6",
59
60
  "react": "^19.2.3",
Binary file
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
@@ -0,0 +1,91 @@
1
+ <svg
2
+ xmlns="http://www.w3.org/2000/svg"
3
+ width="1200"
4
+ height="630"
5
+ viewBox="0 0 1200 630"
6
+ fill="none"
7
+ >
8
+ <defs>
9
+ <linearGradient id="bg" x1="104" y1="44" x2="1090" y2="648" gradientUnits="userSpaceOnUse">
10
+ <stop offset="0" stop-color="#0C1728" />
11
+ <stop offset="0.46" stop-color="#0A1320" />
12
+ <stop offset="1" stop-color="#07101A" />
13
+ </linearGradient>
14
+ <radialGradient id="glow-left" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(168 58) rotate(56.115) scale(498.404 523.217)">
15
+ <stop offset="0" stop-color="#20C5BD" stop-opacity="0.23" />
16
+ <stop offset="0.48" stop-color="#20C5BD" stop-opacity="0.08" />
17
+ <stop offset="1" stop-color="#20C5BD" stop-opacity="0" />
18
+ </radialGradient>
19
+ <radialGradient id="glow-right" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(1086 30) rotate(112.105) scale(468.802 625.774)">
20
+ <stop offset="0" stop-color="#4CA9FF" stop-opacity="0.17" />
21
+ <stop offset="0.44" stop-color="#4CA9FF" stop-opacity="0.06" />
22
+ <stop offset="1" stop-color="#4CA9FF" stop-opacity="0" />
23
+ </radialGradient>
24
+ <linearGradient id="frame" x1="160" y1="86" x2="1084" y2="544" gradientUnits="userSpaceOnUse">
25
+ <stop offset="0" stop-color="#27445F" stop-opacity="0.9" />
26
+ <stop offset="1" stop-color="#1CCAC0" stop-opacity="0.62" />
27
+ </linearGradient>
28
+ <linearGradient id="glyph" x1="0" y1="0" x2="1" y2="1">
29
+ <stop offset="0" stop-color="#28EAD3" />
30
+ <stop offset="1" stop-color="#1CCAC0" />
31
+ </linearGradient>
32
+ <linearGradient id="chart" x1="706" y1="430" x2="1110" y2="236" gradientUnits="userSpaceOnUse">
33
+ <stop offset="0" stop-color="#20C5BD" stop-opacity="0.22" />
34
+ <stop offset="0.38" stop-color="#20C5BD" stop-opacity="0.95" />
35
+ <stop offset="1" stop-color="#7CE8E2" stop-opacity="0.95" />
36
+ </linearGradient>
37
+ <pattern id="grid" width="36" height="36" patternUnits="userSpaceOnUse">
38
+ <path d="M36 0H0V36" stroke="#203347" stroke-opacity="0.32" />
39
+ </pattern>
40
+ </defs>
41
+
42
+ <rect width="1200" height="630" fill="url(#bg)" />
43
+ <rect width="1200" height="630" fill="url(#glow-left)" />
44
+ <rect width="1200" height="630" fill="url(#glow-right)" />
45
+ <rect width="1200" height="630" fill="url(#grid)" opacity="0.62" />
46
+ <rect x="64" y="64" width="1072" height="502" rx="34" fill="#0E1726" fill-opacity="0.55" stroke="url(#frame)" />
47
+ <rect x="96" y="96" width="148" height="38" rx="19" fill="#112233" stroke="#27445F" />
48
+ <text x="129" y="121" fill="#9EEDE8" font-family="Arial, Helvetica, sans-serif" font-size="15" font-weight="700" letter-spacing="2.3">
49
+ APP UI
50
+ </text>
51
+
52
+ <g transform="translate(96 170)">
53
+ <rect x="0" y="0" width="84" height="84" rx="22" fill="#0A1624" stroke="#235768" stroke-opacity="0.8" stroke-width="2" />
54
+ <rect x="0" y="0" width="84" height="84" rx="22" fill="url(#glow-left)" opacity="0.22" />
55
+ <path d="M24 54L40 38L49 47L67 29" stroke="url(#glyph)" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" />
56
+ <path d="M59 29H67V37" stroke="url(#glyph)" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" />
57
+ </g>
58
+
59
+ <text x="96" y="320" fill="#EEF6FF" font-family="Arial, Helvetica, sans-serif" font-size="104" font-weight="700" letter-spacing="-4">
60
+ Trade
61
+ </text>
62
+ <text x="442" y="320" fill="#20C5BD" font-family="Arial, Helvetica, sans-serif" font-size="104" font-weight="700" letter-spacing="-4">
63
+ JS
64
+ </text>
65
+
66
+ <text x="96" y="382" fill="#EEF6FF" font-family="Arial, Helvetica, sans-serif" font-size="44" font-weight="700" letter-spacing="-1.2">
67
+ App
68
+ </text>
69
+ <text x="96" y="436" fill="#A8BED5" font-family="Arial, Helvetica, sans-serif" font-size="30" font-weight="400">
70
+ Dashboards, backtests, charts, derivatives, and runtime data in one UI.
71
+ </text>
72
+
73
+ <rect x="96" y="474" width="188" height="46" rx="23" fill="#102133" stroke="#28516B" />
74
+ <text x="128" y="504" fill="#DAF6F4" font-family="Arial, Helvetica, sans-serif" font-size="21" font-weight="700" letter-spacing="0.4">
75
+ @tradejs/app
76
+ </text>
77
+
78
+ <rect x="686" y="162" width="392" height="250" rx="28" fill="#101B2A" fill-opacity="0.72" stroke="#1F364C" />
79
+ <path d="M728 346L806 288L880 318L970 246L1048 198" stroke="url(#chart)" stroke-width="10" stroke-linecap="round" stroke-linejoin="round" />
80
+ <path d="M1048 198H1090V240" stroke="url(#chart)" stroke-width="10" stroke-linecap="round" stroke-linejoin="round" />
81
+ <circle cx="806" cy="288" r="10" fill="#20C5BD" />
82
+ <circle cx="970" cy="246" r="10" fill="#20C5BD" />
83
+ <circle cx="1048" cy="198" r="11" fill="#7CE8E2" />
84
+ <rect x="730" y="370" width="120" height="14" rx="7" fill="#173046" />
85
+ <rect x="730" y="404" width="208" height="14" rx="7" fill="#12283C" />
86
+ <rect x="730" y="438" width="178" height="14" rx="7" fill="#12283C" />
87
+
88
+ <text x="96" y="560" fill="#7F93AC" font-family="Arial, Helvetica, sans-serif" font-size="20" font-weight="600" letter-spacing="1.2">
89
+ NEXT.JS UI PACKAGE FOR TRADEJS WORKFLOWS
90
+ </text>
91
+ </svg>
Binary file
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>
@@ -6,6 +6,7 @@ import {
6
6
  SystemMessage,
7
7
  } from '@langchain/core/messages';
8
8
  import { toJson } from '@tradejs/core/data';
9
+ import { getOpenRouterModelKwargs } from '@tradejs/node/ai';
9
10
  import { getConnectorCreatorByProvider } from '@tradejs/node/connectors';
10
11
  import {
11
12
  AIChatHistory,
@@ -15,6 +16,8 @@ import {
15
16
  } from '@tradejs/types';
16
17
  import { getFile, setFile } from '@tradejs/infra/files';
17
18
  import { logger } from '@tradejs/infra/logger';
19
+ import { getUserSettings } from '@tradejs/infra/userSettings';
20
+ import { getCurrentUserName } from '@app/lib/currentUser';
18
21
 
19
22
  export const dynamic = 'force-dynamic';
20
23
 
@@ -67,13 +70,21 @@ const buildMessages = (
67
70
  return messages;
68
71
  };
69
72
 
70
- const invokeChatModel = async (messages: BaseMessage[]) => {
73
+ const invokeChatModel = async (messages: BaseMessage[], userName: string) => {
74
+ const settings = await getUserSettings(userName);
75
+ if (!settings.OPENAI_API_KEY || !settings.OPENAI_API_ENDPOINT) {
76
+ throw new Error(`AI settings are incomplete for user ${userName}`);
77
+ }
78
+
79
+ const modelKwargs = getOpenRouterModelKwargs(settings.OPENAI_API_ENDPOINT);
80
+
71
81
  const model = new ChatOpenAI({
72
82
  temperature: 0.7,
73
83
  modelName: 'gpt-4o',
74
- openAIApiKey: process.env.OPENAI_API_KEY,
84
+ apiKey: settings.OPENAI_API_KEY,
85
+ ...(Object.keys(modelKwargs).length ? { modelKwargs } : {}),
75
86
  configuration: {
76
- baseURL: process.env.OPENAI_API_ENDPOINT || 'https://api.openai.com/v1',
87
+ baseURL: settings.OPENAI_API_ENDPOINT,
77
88
  },
78
89
  });
79
90
 
@@ -82,6 +93,11 @@ const invokeChatModel = async (messages: BaseMessage[]) => {
82
93
 
83
94
  export const GET = async (request: NextRequest) => {
84
95
  try {
96
+ const userName = await getCurrentUserName();
97
+ if (!userName) {
98
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
99
+ }
100
+
85
101
  const symbol = request.nextUrl.searchParams.get('symbol');
86
102
 
87
103
  if (!symbol) {
@@ -104,6 +120,11 @@ export const GET = async (request: NextRequest) => {
104
120
 
105
121
  export const POST = async (request: NextRequest) => {
106
122
  try {
123
+ const userName = await getCurrentUserName();
124
+ if (!userName) {
125
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
126
+ }
127
+
107
128
  const body = await request.json();
108
129
  const { message, filters } = body as {
109
130
  message?: AIChatMessage;
@@ -128,7 +149,7 @@ export const POST = async (request: NextRequest) => {
128
149
  }
129
150
 
130
151
  const byBitConnector = await (connectorCreator as ConnectorCreator)({
131
- userName: 'root',
152
+ userName,
132
153
  });
133
154
 
134
155
  const data = await byBitConnector.kline({
@@ -138,7 +159,7 @@ export const POST = async (request: NextRequest) => {
138
159
 
139
160
  const chatMessages = buildMessages(filters, message, data.slice(-100));
140
161
 
141
- const response = await invokeChatModel(chatMessages);
162
+ const response = await invokeChatModel(chatMessages, userName);
142
163
 
143
164
  const responseMessage: AIChatMessage = {
144
165
  from: 'ai',
@@ -12,6 +12,7 @@ import {
12
12
  Interval,
13
13
  ConnectorCreator,
14
14
  } from '@tradejs/types';
15
+ import { getCurrentUserName } from '@app/lib/currentUser';
15
16
 
16
17
  export const dynamic = 'force-dynamic';
17
18
  const projectRoot =
@@ -67,6 +68,11 @@ export const POST = async (
67
68
  { params }: { params: Promise<Params> },
68
69
  ) => {
69
70
  try {
71
+ const userName = await getCurrentUserName();
72
+ if (!userName) {
73
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
74
+ }
75
+
70
76
  const { provider, symbol, interval } = await params;
71
77
  const body = await request.json();
72
78
  const options = body as
@@ -87,7 +93,7 @@ export const POST = async (
87
93
  throw new Error('No connector available for provider');
88
94
  }
89
95
  const connector = await (connectorCreator as ConnectorCreator)({
90
- userName: 'root',
96
+ userName,
91
97
  });
92
98
 
93
99
  const baseData = await connector.kline({
@@ -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: 'root',
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: 'root',
28
+ userName,
23
29
  });
24
30
 
25
31
  const data = await byBitConnector.getTickers();