15 namespace Fossology\UI\Api;
17 $GLOBALS[
'apiCall'] =
true;
20 require_once dirname(__DIR__, 3) .
"/vendor/autoload.php";
21 require_once dirname(__FILE__, 4) .
"/lib/php/bootstrap.php";
53 use Psr\Http\Message\ServerRequestInterface;
54 use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
55 use Psr\Log\LoggerInterface;
56 use Slim\Exception\HttpMethodNotAllowedException;
57 use Slim\Exception\HttpNotFoundException;
58 use Slim\Factory\AppFactory;
59 use Slim\Middleware\ContentLengthMiddleware;
60 use Slim\Psr7\Request;
61 use Slim\Psr7\Response;
65 function getVersionFromUri ($uri)
68 preg_match(
'/\/repo\/api\/v(\d+)/', $uri, $matches);
69 return isset($matches[1]) ? intval($matches[1]) : null;
73 $requestedVersion = isset($_SERVER[
'REQUEST_URI']) ? getVersionFromUri($_SERVER[
'REQUEST_URI']) : null;
74 $apiVersion = in_array($requestedVersion, [ApiVersion::V1, ApiVersion::V2]) ? $requestedVersion : ApiVersion::V1;
77 $BASE_PATH =
"/repo/api/v" .$apiVersion;
79 const AUTH_METHOD =
"JWT_TOKEN";
81 $GLOBALS[
'apiBasePath'] = $BASE_PATH;
83 $startTime = microtime(
true);
90 $timingLogger = $container->get(
"log.timing");
91 $timingLogger->logWithStartTime(
"bootstrap", $startTime);
94 $loader = $container->get(
'twig.loader');
95 $loader->addPath(dirname(__FILE__, 2) .
'/template');
99 $error =
ConfigInit($GLOBALS[
'SYSCONFDIR'], $SysConf,
false);
103 $dbConnected =
false;
106 $timingLogger->toc(
"setup init");
108 $timingLogger->tic();
113 AppFactory::setContainer($container);
114 AppFactory::setResponseFactory(
new ResponseFactoryHelper());
115 $app = AppFactory::create();
116 $app->setBasePath($BASE_PATH);
119 $apiVersionMiddleware =
function (Request $request, RequestHandler $handler) use ($apiVersion) {
120 $request = $request->withAttribute(ApiVersion::ATTRIBUTE_NAME, $apiVersion);
121 return $handler->handle($request);
140 $app->add(
new FossologyInitMiddleware());
142 $app->add(
new RestAuthMiddleware());
144 $app->add(
new ContentLengthMiddleware());
146 $app->add($apiVersionMiddleware);
150 $app->get(
'/health',
function($req, $res) {
151 $handler =
new InfoController($GLOBALS[
'container']);
152 return $handler->getHealth($req, $res, -1);
155 $app->any(
'{route:.*}',
function(ServerRequestInterface $req, ResponseHelper $res) {
156 $error =
new Info(503,
"Unable to connect to DB.", InfoType::ERROR);
157 return $res->withJson($error->getArray(), $error->getCode());
166 $app->options(
'/{routes:.+}', AuthController::class .
':optionsVerification');
169 $app->post(
'/tokens', AuthController::class .
':createNewJwtToken');
172 $app->group(
'/uploads',
173 function (\Slim\Routing\RouteCollectorProxy $app) {
174 $app->get(
'[/{id:\\d+}]', UploadController::class .
':getUploads');
175 $app->delete(
'/{id:\\d+}', UploadController::class .
':deleteUpload');
176 $app->patch(
'/{id:\\d+}', UploadController::class .
':updateUpload');
177 $app->put(
'/{id:\\d+}', UploadController::class .
':moveUpload');
178 $app->post(
'', UploadController::class .
':postUpload');
179 $app->put(
'/{id:\\d+}/permissions', UploadController::class .
':setUploadPermissions');
180 $app->get(
'/{id:\\d+}/perm-groups', UploadController::class .
':getGroupsWithPermissions');
181 $app->get(
'/{id:\\d+}/groups/permission', UploadController::class .
':getGroupsWithPermissions');
182 $app->get(
'/{id:\\d+}/summary', UploadController::class .
':getUploadSummary');
183 $app->get(
'/{id:\\d+}/agents', UploadController::class .
':getAllAgents');
184 $app->get(
'/{id:\\d+}/agents/revision', UploadController::class .
':getAgentsRevision');
185 $app->get(
'/{id:\\d+}/licenses', UploadController::class .
':getUploadLicenses');
186 $app->get(
'/{id:\\d+}/licenses/histogram', UploadController::class .
':getLicensesHistogram');
187 $app->get(
'/{id:\\d+}/licenses/edited', UploadController::class .
':getEditedLicenses');
188 $app->get(
'/{id:\\d+}/licenses/reuse', UploadController::class .
':getReuseReportSummary');
189 $app->get(
'/{id:\\d+}/licenses/scanned', UploadController::class .
':getScannedLicenses');
190 $app->get(
'/{id:\\d+}/licenses/main', UploadController::class .
':getMainLicenses');
191 $app->post(
'/{id:\\d+}/licenses/main', UploadController::class .
':setMainLicense');
192 $app->get(
'/{id:\\d+}/download', UploadController::class .
':uploadDownload');
193 $app->get(
'/{id:\\d+}/clearing-progress', UploadController::class .
':getClearingProgressInfo');
194 $app->delete(
'/{id:\\d+}/licenses/{shortName:[\\w\\- \\.]+}/main', UploadController::class .
':removeMainLicense');
195 $app->get(
'/{id:\\d+}/topitem', UploadController::class .
':getTopItem');
196 $app->put(
'/{id:\\d+}/item/{itemId:\\d+}/licenses', UploadTreeController::class .
':handleAddEditAndDeleteLicenseDecision');
197 $app->get(
'/{id:\\d+}/item/{itemId:\\d+}/view', UploadTreeController::class.
':viewLicenseFile');
198 $app->get(
'/{id:\\d+}/item/{itemId:\\d+}/prev-next', UploadTreeController::class .
':getNextPreviousItem');
199 $app->get(
'/{id:\\d+}/item/{itemId:\\d+}/licenses', UploadTreeController::class .
':getLicenseDecisions');
200 $app->put(
'/{id:\\d+}/item/{itemId:\\d+}/clearing-decision', UploadTreeController::class .
':setClearingDecision');
201 $app->get(
'/{id:\\d+}/item/{itemId:\\d+}/bulk-history', UploadTreeController::class .
':getBulkHistory');
202 $app->get(
'/{id:\\d+}/item/{itemId:\\d+}/clearing-history', UploadTreeController::class .
':getClearingHistory');
203 $app->get(
'/{id:\\d+}/item/{itemId:\\d+}/highlight', UploadTreeController::class .
':getHighlightEntries');
204 $app->get(
'/{id:\\d+}/item/{itemId:\\d+}/totalcopyrights', CopyrightController::class .
':getTotalFileCopyrights');
205 $app->get(
'/{id:\\d+}/item/{itemId:\\d+}/tree/view', UploadTreeController::class .
':getTreeView');
206 $app->get(
'/{id:\\d+}/item/{itemId:\\d+}/info', FileInfoController::class .
':getItemInfo');
207 $app->post(
'/{id:\\d+}/item/{itemId:\\d+}/bulk-scan', UploadTreeController::class .
':scheduleBulkScan');
208 $app->get(
'/{id:\\d+}/conf', ConfController::class .
':getConfInfo');
209 $app->put(
'/{id:\\d+}/conf', ConfController::class .
':updateConfData');
210 $app->get(
'/{id:\\d+}/copyrights', UploadController::class .
':getUploadCopyrights');
212 $app->group(
'/{id:\\d+}/item/{itemId:\\d+}',
function (\Slim\Routing\RouteCollectorProxy $app) {
213 $app->get(
'/copyrights', CopyrightController::class .
':getFileCopyrights');
214 $app->delete(
'/copyrights/{hash:.*}', CopyrightController::class .
':deleteFileCopyright');
215 $app->patch(
'/copyrights/{hash:.*}', CopyrightController::class .
':restoreFileCopyright');
216 $app->put(
'/copyrights/{hash:.*}', CopyrightController::class .
':updateFileCopyright');
217 $app->get(
'/emails', CopyrightController::class .
':getFileEmail');
218 $app->delete(
'/emails/{hash:.*}', CopyrightController::class .
':deleteFileEmail');
219 $app->patch(
'/emails/{hash:.*}', CopyrightController::class .
':restoreFileEmail');
220 $app->put(
'/emails/{hash:.*}', CopyrightController::class .
':updateFileEmail');
221 $app->get(
'/urls', CopyrightController::class .
':getFileUrl');
222 $app->delete(
'/urls/{hash:.*}', CopyrightController::class .
':deleteFileUrl');
223 $app->patch(
'/urls/{hash:.*}', CopyrightController::class .
':restoreFileUrl');
224 $app->put(
'/urls/{hash:.*}', CopyrightController::class .
':updateFileUrl');
225 $app->get(
'/authors', CopyrightController::class .
':getFileAuthor');
226 $app->delete(
'/authors/{hash:.*}', CopyrightController::class .
':deleteFileAuthor');
227 $app->patch(
'/authors/{hash:.*}', CopyrightController::class .
':restoreFileAuthor');
228 $app->put(
'/authors/{hash:.*}', CopyrightController::class .
':updateFileAuthor');
229 $app->get(
'/eccs', CopyrightController::class .
':getFileEcc');
230 $app->delete(
'/eccs/{hash:.*}', CopyrightController::class .
':deleteFileEcc');
231 $app->patch(
'/eccs/{hash:.*}', CopyrightController::class .
':restoreFileEcc');
232 $app->put(
'/eccs/{hash:.*}', CopyrightController::class .
':updateFileEcc');
233 $app->get(
'/keywords', CopyrightController::class .
':getFileKeyword');
234 $app->delete(
'/keywords/{hash:.*}', CopyrightController::class .
':deleteFileKeyword');
235 $app->patch(
'/keywords/{hash:.*}', CopyrightController::class .
':restoreFileKeyword');
236 $app->put(
'/keywords/{hash:.*}', CopyrightController::class .
':updateFileKeyword');
237 $app->get(
'/ipras', CopyrightController::class .
':getFileIpra');
238 $app->delete(
'/ipras/{hash:.*}', CopyrightController::class .
':deleteFileIpra');
239 $app->patch(
'/ipras/{hash:.*}', CopyrightController::class .
':restoreFileIpra');
240 $app->put(
'/ipras/{hash:.*}', CopyrightController::class .
':updateFileIpra');
242 $app->any(
'/{params:.*}', BadRequestController::class);
247 $app->group(
'/users',
248 function (\Slim\Routing\RouteCollectorProxy $app) {
249 $app->get(
'[/{id:\\d+}]', UserController::class .
':getUsers');
250 $app->put(
'[/{id:\\d+}]', UserController::class .
':updateUser');
251 $app->post(
'', UserController::class .
':addUser');
252 $app->delete(
'/{id:\\d+}', UserController::class .
':deleteUser');
253 $app->get(
'/self', UserController::class .
':getCurrentUser');
254 $app->post(
'/tokens', UserController::class .
':createRestApiToken');
255 $app->get(
'/tokens/{type:\\w+}', UserController::class .
':getTokens');
256 $app->any(
'/{params:.*}', BadRequestController::class);
260 $app->group(
'/obligations',
261 function (\Slim\Routing\RouteCollectorProxy $app) {
262 $app->get(
'/list', ObligationController::class .
':obligationsList');
263 $app->get(
'/{id:\\d+}', ObligationController::class .
':obligationsDetails');
264 $app->get(
'', ObligationController::class .
':obligationsAllDetails');
265 $app->delete(
'/{id:\\d+}', ObligationController::class .
':deleteObligation');
266 $app->get(
'/export-csv', ObligationController::class .
':exportObligationsToCSV');
267 $app->post(
'/import-csv', ObligationController::class .
':importObligationsFromCSV');
268 $app->any(
'/{params:.*}', BadRequestController::class);
272 $app->group(
'/groups',
273 function (\Slim\Routing\RouteCollectorProxy $app) {
274 $app->get(
'', GroupController::class .
':getGroups');
275 $app->post(
'', GroupController::class .
':createGroup');
276 $app->post(
'/{id:\\d+}/user/{userId:\\d+}', GroupController::class .
':addMember');
277 $app->delete(
'/{id:\\d+}', GroupController::class .
':deleteGroup');
278 $app->delete(
'/{id:\\d+}/user/{userId:\\d+}', GroupController::class .
':deleteGroupMember');
279 $app->get(
'/deletable', GroupController::class .
':getDeletableGroups');
280 $app->get(
'/{id:\\d+}/members', GroupController::class .
':getGroupMembers');
281 $app->put(
'/{id:\\d+}/user/{userId:\\d+}', GroupController::class .
':changeUserPermission');
282 $app->any(
'/{params:.*}', BadRequestController::class);
287 function (\Slim\Routing\RouteCollectorProxy $app) {
288 $app->get(
'[/{id:\\d+}]', JobController::class .
':getJobs');
289 $app->get(
'/all', JobController::class .
':getAllJobs');
290 $app->get(
'/dashboard/statistics', JobController::class .
':getJobStatistics');
291 $app->get(
'/scheduler/operation/{operationName:[\\w\\- \\.]+}', JobController::class .
':getSchedulerJobOptionsByOperation');
292 $app->post(
'/scheduler/operation/run', JobController::class .
':handleRunSchedulerOption');
293 $app->post(
'', JobController::class .
':createJob');
294 $app->get(
'/history', JobController::class .
':getJobsHistory');
295 $app->get(
'/dashboard', JobController::class .
':getAllServerJobsStatus');
296 $app->delete(
'/{id:\\d+}/{queue:\\d+}', JobController::class .
':deleteJob');
297 $app->any(
'/{params:.*}', BadRequestController::class);
301 $app->group(
'/search',
302 function (\Slim\Routing\RouteCollectorProxy $app) {
303 $app->get(
'', SearchController::class .
':performSearch');
307 $app->group(
'/maintenance',
308 function (\Slim\Routing\RouteCollectorProxy $app) {
309 $app->post(
'', MaintenanceController::class .
':createMaintenance');
310 $app->any(
'/{params:.*}', BadRequestController::class);
315 $app->group(
'/folders',
316 function (\Slim\Routing\RouteCollectorProxy $app) {
317 $app->get(
'[/{id:\\d+}]', FolderController::class .
':getFolders');
318 $app->post(
'', FolderController::class .
':createFolder');
319 $app->delete(
'/{id:\\d+}', FolderController::class .
':deleteFolder');
320 $app->patch(
'/{id:\\d+}', FolderController::class .
':editFolder');
321 $app->put(
'/{id:\\d+}', FolderController::class .
':copyFolder');
322 $app->get(
'/{id:\\d+}/contents/unlinkable', FolderController::class .
':getUnlinkableFolderContents');
323 $app->put(
'/contents/{contentId:\\d+}/unlink', FolderController::class .
':unlinkFolder');
324 $app->get(
'/{id:\\d+}/contents', FolderController::class .
':getAllFolderContents');
325 $app->any(
'/{params:.*}', BadRequestController::class);
329 $app->group(
'/report',
330 function (\Slim\Routing\RouteCollectorProxy $app) {
331 $app->get(
'', ReportController::class .
':getReport');
332 $app->get(
'/{id:\\d+}', ReportController::class .
':downloadReport');
333 $app->post(
'/import', ReportController::class .
':importReport');
334 $app->any(
'/{params:.*}', BadRequestController::class);
338 $app->group(
'/customise',
339 function (\Slim\Routing\RouteCollectorProxy $app) {
340 $app->get(
'', CustomiseController::class .
':getCustomiseData');
341 $app->put(
'', CustomiseController::class .
':updateCustomiseData');
342 $app->get(
'/banner', CustomiseController::class .
':getBannerMessage');
343 $app->any(
'/{params:.*}', BadRequestController::class);
348 function (\Slim\Routing\RouteCollectorProxy $app) {
349 $app->get(
'', InfoController::class .
':getInfo');
351 $app->group(
'/health',
352 function (\Slim\Routing\RouteCollectorProxy $app) {
353 $app->get(
'', InfoController::class .
':getHealth');
355 $app->group(
'/openapi',
356 function (\Slim\Routing\RouteCollectorProxy $app) {
357 $app->get(
'', InfoController::class .
':getOpenApi');
361 $app->group(
'/filesearch',
362 function (\Slim\Routing\RouteCollectorProxy $app) {
363 $app->post(
'', FileSearchController::class .
':getFiles');
364 $app->any(
'/{params:.*}', BadRequestController::class);
368 $app->group(
'/license',
369 function (\Slim\Routing\RouteCollectorProxy $app) {
370 $app->get(
'', LicenseController::class .
':getAllLicenses');
371 $app->post(
'/import-csv', LicenseController::class .
':handleImportLicense');
372 $app->get(
'/export-csv', LicenseController::class .
':exportAdminLicenseToCSV');
373 $app->post(
'', LicenseController::class .
':createLicense');
374 $app->put(
'/verify/{shortname:.+}', LicenseController::class .
':verifyLicense');
375 $app->put(
'/merge/{shortname:.+}', LicenseController::class .
':mergeLicense');
376 $app->get(
'/admincandidates', LicenseController::class .
':getCandidates');
377 $app->get(
'/adminacknowledgements', LicenseController::class .
':getAllAdminAcknowledgements');
378 $app->get(
'/stdcomments', LicenseController::class .
':getAllLicenseStandardComments');
379 $app->put(
'/stdcomments', LicenseController::class .
':handleLicenseStandardComment');
380 $app->post(
'/suggest', LicenseController::class .
':getSuggestedLicense');
381 $app->get(
'/{shortname:.+}', LicenseController::class .
':getLicense');
382 $app->patch(
'/{shortname:.+}', LicenseController::class .
':updateLicense');
383 $app->delete(
'/admincandidates/{id:\\d+}',
384 LicenseController::class .
':deleteAdminLicenseCandidate');
385 $app->put(
'/adminacknowledgements', LicenseController::class .
':handleAdminLicenseAcknowledgement');
386 $app->any(
'/{params:.*}', BadRequestController::class);
390 $app->group(
'/overview',
391 function (\Slim\Routing\RouteCollectorProxy $app) {
392 $app->get(
'/database/contents', OverviewController::class .
':getDatabaseContents');
393 $app->get(
'/disk/usage', OverviewController::class .
':getDiskSpaceUsage');
394 $app->get(
'/info/php', OverviewController::class .
':getPhpInfo');
395 $app->get(
'/database/metrics', OverviewController::class .
':getDatabaseMetrics');
396 $app->get(
'/queries/active', OverviewController::class .
':getActiveQueries');
397 $app->any(
'/{params:.*}', BadRequestController::class);
402 $customErrorHandler =
function (
403 ServerRequestInterface $request,
404 Throwable $exception,
405 bool $displayErrorDetails,
407 bool $logErrorDetails,
408 ?LoggerInterface $logger = null
410 if ($logger ===
null) {
411 $logger = $app->getContainer()->get(
'logger');
414 $logger->error($exception->getMessage(), $exception->getTrace());
416 if ($displayErrorDetails) {
417 $payload = [
'error'=> $exception->getMessage(),
418 'trace' => $exception->getTraceAsString()];
420 $error =
new Info(500,
"Something went wrong! Please try again later.",
422 $payload = $error->getArray();
425 $response = $app->getResponseFactory()->createResponse(500)
426 ->withHeader(
"Content-Type",
"application/json");
427 $response->getBody()->write(
428 json_encode($payload, JSON_UNESCAPED_UNICODE)
435 $errorMiddleware = $app->addErrorMiddleware(
false,
true,
true,
436 $container->get(
"logger"));
439 $errorMiddleware->setErrorHandler(
440 HttpNotFoundException::class,
441 function (ServerRequestInterface $request, Throwable $exception,
bool $displayErrorDetails) {
442 $response =
new ResponseHelper();
443 $error =
new Info(404,
"Resource not found", InfoType::ERROR);
444 $response = $response->withJson($error->getArray(), $error->getCode());
450 $errorMiddleware->setErrorHandler(
451 HttpMethodNotAllowedException::class,
452 function (ServerRequestInterface $request, Throwable $exception,
bool $displayErrorDetails) {
453 $response =
new Response();
454 $response->getBody()->write(
'405 NOT ALLOWED');
456 $response = $response->withStatus(405);
462 $errorMiddleware->setErrorHandler(
463 HttpErrorException::class,
464 function (ServerRequestInterface $request, HttpErrorException $exception,
bool $displayErrorDetails) {
465 $response =
new ResponseHelper();
466 $error =
new Info($exception->getCode(), $exception->getMessage(),
468 $response = $response->withJson($error->getArray(), $error->getCode());
469 if (!empty($exception->getHeaders())) {
470 foreach ($exception->getHeaders() as $key => $value) {
471 $response = $response->withHeader($key, $value);
479 $errorMiddleware->setDefaultErrorHandler($customErrorHandler);
483 $GLOBALS[
'container']->get(
"db.manager")->flushStats();
Controller for Auth requests.
Controller for file searching.
Controller for Folder model.
Controller for Group model.
Controller for REST API version.
Controller for Job model.
Controller for OverviewController model.
Controller for Maintenance model.
Controller for Search model.
Controller for Upload model.
Controller for UploadTree model.
Controller for User model.
static addCorsHeaders(ResponseInterface $response)
Override Slim response factory for custom response.
Override Slim response for withJson function.
Middleware to initialize FOSSology for Slim framework.
Authentication middleware for Slim framework.
Different type of infos provided by REST.
Info model to contain general error and return values.
plugin_load()
Load every module ui found in mods-enabled.
ConfigInit($sysconfdir, &$SysConf, $exitOnDbFail=true)
Initialize the fossology system after bootstrap().
bootstrap($sysconfdir="")
Bootstrap the fossology php library.