Skip to content

Commit ab1755b

Browse files
committed
Improve a bit the project structure.
1 parent b071085 commit ab1755b

16 files changed

+414
-393
lines changed

src/api.ts

+25-305
Original file line numberDiff line numberDiff line change
@@ -1,307 +1,27 @@
1-
import { spawn } from 'child_process';
2-
import colors from 'colors';
3-
import { NextFunction, Request, Response } from 'express';
4-
import { hostname } from 'os';
5-
import * as path from 'path';
6-
7-
import deployDeleteController from './controller/delete';
8-
import uploadController from './controller/upload';
9-
10-
import {
11-
DeleteBody,
12-
DeployBody,
13-
Deployment,
14-
FetchBranchListBody,
15-
FetchFilesFromRepoBody,
16-
ProtocolMessageType,
17-
WorkerMessage,
18-
WorkerMessageUnknown,
19-
allApplications,
20-
childProcesses,
21-
deploymentMap
22-
} from './constants';
23-
24-
import AppError from './utils/appError';
1+
import { callFunction } from './controller/call';
2+
import { deployDelete } from './controller/delete';
3+
import { deploy } from './controller/deploy';
4+
import { globalError } from './controller/error';
5+
import { logs } from './controller/logs';
256
import {
26-
catchAsync,
27-
deleteRepoFolderIfExist,
28-
dirName,
29-
ensureFolderExists,
30-
execPromise,
31-
exists,
32-
installDependencies,
33-
isIAllApps,
34-
logProcessOutput
35-
} from './utils/utils';
36-
37-
import { PackageError } from '@metacall/protocol/package';
38-
import { appsDirectory } from './utils/config';
39-
40-
const appsDir = appsDirectory();
41-
42-
colors.enable();
43-
44-
export const callFnByName = (
45-
req: Request,
46-
res: Response,
47-
next: NextFunction
48-
): Response | void => {
49-
if (!(req.params && req.params.name))
50-
next(
51-
new AppError(
52-
'A function name is required in the path; i.e: /call/sum.',
53-
404
54-
)
55-
);
56-
57-
const { appName: app, name } = req.params;
58-
const args = Object.values(req.body);
59-
60-
if (!(app in childProcesses)) {
61-
return res
62-
.status(404)
63-
.send(
64-
`Oops! It looks like the application (${app}) hasn't been deployed yet. Please deploy it before you can call its functions.`
65-
);
66-
}
67-
68-
let responseSent = false; // Flag to track if response has been sent
69-
let errorCame = false;
70-
71-
childProcesses[app].send({
72-
type: ProtocolMessageType.Invoke,
73-
data: {
74-
name,
75-
args
76-
}
77-
});
78-
79-
childProcesses[app].on('message', (message: WorkerMessageUnknown) => {
80-
if (!responseSent) {
81-
// Check if response has already been sent
82-
if (message.type === ProtocolMessageType.InvokeResult) {
83-
responseSent = true; // Set flag to true to indicate response has been sent
84-
return res.send(JSON.stringify(message.data));
85-
} else {
86-
errorCame = true;
87-
}
88-
}
89-
});
90-
91-
// Default response in case the 'message' event is not triggered
92-
if (!responseSent && errorCame) {
93-
responseSent = true; // Set flag to true to indicate response has been sent
94-
errorCame = false;
95-
return res.send('Function calling error');
96-
}
7+
fetchBranchList,
8+
fetchFileList,
9+
fetchFilesFromRepo
10+
} from './controller/repository';
11+
import { serveStatic } from './controller/static';
12+
import { uploadPackage } from './controller/upload';
13+
import { validate } from './controller/validate';
14+
15+
export default {
16+
callFunction,
17+
deployDelete,
18+
deploy,
19+
globalError,
20+
logs,
21+
fetchFilesFromRepo,
22+
fetchBranchList,
23+
fetchFileList,
24+
serveStatic,
25+
uploadPackage,
26+
validate
9727
};
98-
99-
export const serveStatic = catchAsync(
100-
async (req: Request, res: Response, next: NextFunction): Promise<void> => {
101-
if (!req.params) next(new AppError('Invalid API endpoint', 404));
102-
103-
const { app, file } = req.params;
104-
105-
const appLocation = path.join(appsDir, `${app}/${file}`);
106-
107-
if (!(app in childProcesses)) {
108-
next(
109-
new AppError(
110-
`Oops! It looks like the application (${app}) hasn't been deployed yet. Please deploy it before you can call its functions.`,
111-
404
112-
)
113-
);
114-
}
115-
116-
if (!(await exists(appLocation)))
117-
next(
118-
new AppError(
119-
"The file you're looking for might not be available or the application may not be deployed.",
120-
404
121-
)
122-
);
123-
124-
return res.status(200).sendFile(appLocation);
125-
}
126-
);
127-
128-
export const fetchFiles = (
129-
req: Request,
130-
res: Response,
131-
next: NextFunction
132-
): void => uploadController(req, res, next);
133-
134-
export const fetchFilesFromRepo = catchAsync(
135-
async (
136-
req: Omit<Request, 'body'> & { body: FetchFilesFromRepoBody },
137-
res: Response,
138-
next: NextFunction
139-
) => {
140-
const { branch, url } = req.body;
141-
142-
await ensureFolderExists(appsDir);
143-
144-
try {
145-
await deleteRepoFolderIfExist(appsDir, url);
146-
} catch (err) {
147-
next(
148-
new AppError(
149-
'error occurred in deleting repository directory',
150-
500
151-
)
152-
);
153-
}
154-
155-
await execPromise(
156-
`cd ${appsDir}; git clone --single-branch --depth=1 --branch ${branch} ${url} `
157-
);
158-
159-
const id = dirName(req.body.url);
160-
161-
// TODO: This method is wrong
162-
// deployment.id = id;
163-
// deployment.path = `${appsDir}/${id}`;
164-
165-
return res.status(201).send({ id });
166-
}
167-
);
168-
169-
export const fetchBranchList = catchAsync(
170-
async (
171-
req: Omit<Request, 'body'> & { body: FetchBranchListBody },
172-
res: Response
173-
) => {
174-
const { stdout } = await execPromise(
175-
`git ls-remote --heads ${req.body.url}`
176-
);
177-
178-
const branches: string[] = [];
179-
180-
JSON.stringify(stdout.toString())
181-
.split('\\n')
182-
.forEach(el => {
183-
if (el.trim().length > 1) {
184-
branches.push(el.split('refs/heads/')[1]);
185-
}
186-
});
187-
188-
return res.send({ branches });
189-
}
190-
);
191-
192-
export const fetchFileList = catchAsync(
193-
async (
194-
req: Omit<Request, 'body'> & { body: FetchFilesFromRepoBody },
195-
res: Response,
196-
next: NextFunction
197-
) => {
198-
await ensureFolderExists(appsDir);
199-
200-
try {
201-
await deleteRepoFolderIfExist(appsDir, req.body.url);
202-
} catch (err) {
203-
next(
204-
new AppError(
205-
'error occurred in deleting repository directory',
206-
500
207-
)
208-
);
209-
}
210-
await execPromise(
211-
`cd ${appsDir} ; git clone ${req.body.url} --depth=1 --no-checkout`
212-
);
213-
214-
const dirPath = `${appsDir}/${dirName(req.body.url)}`;
215-
216-
const { stdout } = await execPromise(
217-
`cd ${dirPath} ; git ls-tree -r ${req.body.branch} --name-only; cd .. ; rm -r ${dirPath}`
218-
);
219-
220-
return res.send({
221-
files: JSON.stringify(stdout.toString()).split('\\n')
222-
});
223-
}
224-
);
225-
226-
export const deploy = catchAsync(
227-
async (
228-
req: Omit<Request, 'body'> & { body: DeployBody },
229-
res: Response,
230-
next: NextFunction
231-
) => {
232-
try {
233-
// TODO: Implement repository
234-
// req.body.resourceType == 'Repository' &&
235-
// (await calculatePackages(next));
236-
237-
const deployment = deploymentMap[req.body.suffix];
238-
239-
if (deployment === undefined) {
240-
return next(
241-
new AppError(
242-
`Invalid deployment id: ${req.body.suffix}`,
243-
400
244-
)
245-
);
246-
}
247-
248-
await installDependencies(deployment);
249-
250-
const desiredPath = path.join(__dirname, 'worker', 'index.js');
251-
252-
const proc = spawn('metacall', [desiredPath], {
253-
stdio: ['pipe', 'pipe', 'pipe', 'ipc']
254-
});
255-
256-
proc.send({
257-
type: ProtocolMessageType.Load,
258-
data: deployment
259-
});
260-
261-
logProcessOutput(proc.stdout, proc.pid, deployment.id);
262-
logProcessOutput(proc.stderr, proc.pid, deployment.id);
263-
264-
proc.on('message', (payload: WorkerMessageUnknown) => {
265-
if (payload.type === ProtocolMessageType.MetaData) {
266-
const message = payload as WorkerMessage<Deployment>;
267-
if (isIAllApps(message.data)) {
268-
const appName = Object.keys(message.data)[0];
269-
childProcesses[appName] = proc;
270-
allApplications[appName] = message.data[appName];
271-
}
272-
}
273-
});
274-
275-
return res.status(200).json({
276-
suffix: hostname(),
277-
prefix: deployment.id,
278-
version: 'v1'
279-
});
280-
} catch (err) {
281-
// Check if the error is PackageError.Empty
282-
if (err === PackageError.Empty) {
283-
return next(err);
284-
}
285-
return next(err);
286-
}
287-
}
288-
);
289-
290-
export const showLogs = (req: Request, res: Response): Response => {
291-
return res.send('Demo Logs...');
292-
};
293-
294-
export const deployDelete = (
295-
req: Omit<Request, 'body'> & { body: DeleteBody },
296-
res: Response,
297-
next: NextFunction
298-
): void => deployDeleteController(req, res, next);
299-
300-
export const validateAndDeployEnabled = (
301-
req: Request,
302-
res: Response
303-
): Response =>
304-
res.status(200).json({
305-
status: 'success',
306-
data: true
307-
});

src/app.ts

+8-9
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,35 @@ import { hostname } from 'os';
22

33
import express, { NextFunction, Request, Response } from 'express';
44

5-
import * as api from './api';
5+
import api from './api';
66
import { allApplications } from './constants';
77
import AppError from './utils/appError';
8-
import globalErrorHandler from './utils/errorHandler';
98

109
const app = express();
1110
const host = hostname();
1211

1312
app.use(express.json());
1413
app.use(express.urlencoded({ extended: true }));
1514

16-
app.get('/validate', api.validateAndDeployEnabled);
17-
app.get('/api/account/deploy-enabled', api.validateAndDeployEnabled);
15+
app.get('/validate', api.validate);
16+
app.get('/api/account/deploy-enabled', api.validate);
1817

19-
app.post(`/${host}/:appName/:version/call/:name`, api.callFnByName);
18+
app.post(`/${host}/:appName/:version/call/:name`, api.callFunction);
2019
app.get(
2120
`/${host}/:appName/:version/static/.metacall/faas/apps/:app/:file`,
2221
api.serveStatic
2322
);
2423

25-
app.post('/api/package/create', api.fetchFiles);
24+
app.post('/api/package/create', api.uploadPackage);
2625
app.post('/api/repository/add', api.fetchFilesFromRepo);
2726

2827
app.post('/api/repository/branchlist', api.fetchBranchList);
2928
app.post('/api/repository/filelist', api.fetchFileList);
30-
app.post('/api/deploy/logs', api.showLogs);
29+
app.post('/api/deploy/logs', api.logs);
3130

3231
app.post('/api/deploy/create', api.deploy);
3332

34-
app.get('/api/inspect', (req, res) => {
33+
app.get('/api/inspect', (_req, res) => {
3534
res.send(Object.values(allApplications));
3635
});
3736

@@ -42,6 +41,6 @@ app.all('*', (req: Request, res: Response, next: NextFunction) => {
4241
next(new AppError(`Can't find ${req.originalUrl} on this server!`, 404));
4342
});
4443

45-
app.use(globalErrorHandler);
44+
app.use(api.globalError);
4645

4746
export default app;

0 commit comments

Comments
 (0)