Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions apps/backend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# OpenAI API Key
OPENAI_API_KEY=your_openai_api_key_here

# Database file name
DB_FILE_NAME=local.db
4 changes: 3 additions & 1 deletion apps/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@libsql/client": "^0.15.7",
"@server-manager/shared": "workspace:*",
"@types/react-router-dom": "^5.3.3",
"ai": "^5.0.11",
"bcrypt": "^5.1.1",
"bcryptjs": "^3.0.2",
"cors": "^2.8.5",
Expand All @@ -38,10 +39,11 @@
"express": "^4.21.2",
"jsonwebtoken": "^9.0.2",
"morgan": "^1.10.0",
"openai": "^5.12.2",
"react-router-dom": "^7.6.0",
"uuid": "^11.1.0",
"winston": "^3.17.0",
"zod": "^3.24.4"
"zod": "^3.25.76"
},
"devDependencies": {
"@types/bcrypt": "^5.0.2",
Expand Down
2 changes: 2 additions & 0 deletions apps/backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import morganMiddleware from './middlewares/morgan';
import dnsRoutes from './routes/dnsRoutes';
import httpRoutes from './routes/httpRoutes';
import systemMetricsRoutes from './routes/systemMetricsRoutes';
import chatRoutes from './routes/chatRoutes';
import logger from './lib/logger';

const app: Express = express();
Expand All @@ -35,6 +36,7 @@ app.use('/api/users', userRoutes);
app.use('/api/dns', dnsRoutes);
app.use('/api/http', httpRoutes);
app.use('/api/system-metrics', systemMetricsRoutes);
app.use('/api/chat', chatRoutes);

// Error handling
app.use(errorHandler);
Expand Down
33 changes: 33 additions & 0 deletions apps/backend/src/controllers/__tests__/chatController.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import request from 'supertest';
import app from '../../app';
import { streamText } from 'ai';
import type { Request, Response, NextFunction } from 'express';

jest.mock('ai', () => ({
streamText: jest.fn(),
}));

jest.mock('../../middlewares/authMiddleware', () => ({
authMiddleware: (req: Request, res: Response, next: NextFunction) => next(),
}));

describe('ChatController', () => {
it('should call streamText with the correct parameters', async () => {
const mockMessages = [{ role: 'user', content: 'Hello' }];
(streamText as jest.Mock).mockResolvedValue({
toAIStreamResponse: (res: Response) => {
return res.status(200).json({ message: 'mocked response' });
},
});

const response = await request(app)
.post('/api/chat')
.send({ messages: mockMessages });

expect(response.status).toBe(200);
expect(response.body).toEqual({ message: 'mocked response' });
expect(streamText).toHaveBeenCalledWith(expect.objectContaining({
messages: mockMessages,
}));
});
});
27 changes: 27 additions & 0 deletions apps/backend/src/controllers/chatController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { Request, Response } from 'express';
import { streamText } from 'ai';
import { openai } from '../lib/openai';
import { tools } from '../lib/toolRegistry';
import logger from '../lib/logger';

class ChatController {
async handleChat(req: Request, res: Response) {
const { messages } = req.body;

try {
const result = await streamText({
model: openai('gpt-4o'),
messages,
tools,
toolChoice: 'auto',
});

return result.toAIStreamResponse(res);
} catch (error) {
logger.error('Error handling chat request:', error);
res.status(500).json({ message: 'Failed to handle chat request' });
}
}
}

export default new ChatController();
30 changes: 17 additions & 13 deletions apps/backend/src/controllers/dnsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -437,10 +437,11 @@ const reloadBindService = async (): Promise<void> => {
/**
* Update DNS configuration
*/
export const updateDnsConfiguration = async (req: AuthRequest, res: Response) => {
try {
// Validate the request body
const validatedConfig: DnsConfiguration = dnsConfigurationSchema.parse(req.body);
class DnsController {
public async updateDnsConfiguration(req: AuthRequest, res: Response) {
try {
// Validate the request body
const validatedConfig: DnsConfiguration = dnsConfigurationSchema.parse(req.body);

// Convert string values to arrays where needed
if (typeof validatedConfig.listenOn === 'string') {
Expand Down Expand Up @@ -652,12 +653,12 @@ export const updateDnsConfiguration = async (req: AuthRequest, res: Response) =>
}
};

/**
* Get the current DNS configuration
*/
export const getCurrentDnsConfiguration = async (req: AuthRequest, res: Response) => {
try {
logger.debug(`Running in ${isProd ? 'PRODUCTION' : 'DEVELOPMENT'} mode`);
/**
* Get the current DNS configuration
*/
public async getCurrentDnsConfiguration(req: AuthRequest, res: Response) {
try {
logger.debug(`Running in ${isProd ? 'PRODUCTION' : 'DEVELOPMENT'} mode`);
logger.debug(`Using BIND_CONF_PATH: ${BIND_CONF_PATH}`);
logger.debug(`Using ZONE_CONF_PATH: ${ZONE_CONF_PATH}`);

Expand Down Expand Up @@ -824,8 +825,11 @@ export const getCurrentDnsConfiguration = async (req: AuthRequest, res: Response
});
} catch (error) {
logger.error('Error getting DNS configuration:', error);
res.status(500).json({
message: error instanceof Error ? error.message : 'Failed to get DNS configuration'
res.status(500).json({
message: error instanceof Error ? error.message : 'Failed to get DNS configuration'
});
}
};
}
}

export default new DnsController();
Loading
Loading