mirror of https://github.com/iconify/api.git
chore: split api responses from server, allowing reuse, prepare npm package
This commit is contained in:
parent
2181b8022c
commit
ceb3fc4394
|
|
@ -0,0 +1,18 @@
|
|||
/.idea
|
||||
/.vscode
|
||||
.DS_Store
|
||||
/.env
|
||||
/.editorconfig
|
||||
/.prettierrc
|
||||
*.map
|
||||
/docker.sh
|
||||
/Dockerfile
|
||||
/tsconfig*.*
|
||||
/vitest.config.*
|
||||
/.github
|
||||
/src
|
||||
/node_modules
|
||||
/cache
|
||||
/tmp
|
||||
/icons
|
||||
/tests
|
||||
|
|
@ -6,6 +6,12 @@ This repository contains Iconify API script. It is a HTTP server, written in Nod
|
|||
- Generates SVG, which you can link to in HTML or stylesheet.
|
||||
- Provides search engine for hosted icons, which can be used by icon pickers.
|
||||
|
||||
## NPM Package
|
||||
|
||||
This package is also available at NPM, allowing using API code in custom wrappers.
|
||||
|
||||
NPM package contains only compiled files, to build custom Docker image you need to use source files from Git repository, not NPM package.
|
||||
|
||||
## Docker
|
||||
|
||||
To build a Docker image, run `./docker.sh`.
|
||||
|
|
|
|||
|
|
@ -3,8 +3,11 @@
|
|||
"description": "Iconify API",
|
||||
"author": "Vjacheslav Trushkin",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"version": "3.0.2",
|
||||
"version": "3.1.0-beta.1",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"tag": "next"
|
||||
},
|
||||
"type": "module",
|
||||
"bugs": "https://github.com/iconify/api/issues",
|
||||
"homepage": "https://github.com/iconify/api",
|
||||
|
|
|
|||
|
|
@ -125,8 +125,9 @@ export function updateIconSets(): number {
|
|||
/**
|
||||
* Trigger update
|
||||
*/
|
||||
export function triggerIconSetsUpdate() {
|
||||
export function triggerIconSetsUpdate(done?: (success?: boolean) => void) {
|
||||
if (!importers) {
|
||||
done?.();
|
||||
return;
|
||||
}
|
||||
console.log('Checking for updates...');
|
||||
|
|
@ -147,6 +148,10 @@ export function triggerIconSetsUpdate() {
|
|||
.then((updated) => {
|
||||
console.log(updated ? 'Update complete' : 'Nothing to update');
|
||||
updateIconSets();
|
||||
done?.(true);
|
||||
})
|
||||
.catch(console.error);
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
done?.(false);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { checkJSONPQuery, sendJSONResponse } from './json.js';
|
||||
import { createIconsDataResponse } from '../responses/icons.js';
|
||||
|
||||
type CallbackResult = object | number;
|
||||
|
||||
/**
|
||||
* Handle icons data API response
|
||||
*/
|
||||
export function handleIconsDataResponse(
|
||||
prefix: string,
|
||||
wrapJS: boolean,
|
||||
query: FastifyRequest['query'],
|
||||
res: FastifyReply
|
||||
) {
|
||||
const q = (query || {}) as Record<string, string>;
|
||||
|
||||
// Check for JSONP
|
||||
const wrap = checkJSONPQuery(q, wrapJS, 'SimpleSVG._loaderCallback');
|
||||
if (!wrap) {
|
||||
// Invalid JSONP callback
|
||||
res.send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
// Function to send response
|
||||
const respond = (result: CallbackResult) => {
|
||||
if (typeof result === 'number') {
|
||||
res.send(result);
|
||||
} else {
|
||||
sendJSONResponse(result, q, wrap, res);
|
||||
}
|
||||
};
|
||||
|
||||
// Get result
|
||||
const result = createIconsDataResponse(prefix, q);
|
||||
if (result instanceof Promise) {
|
||||
result.then(respond).catch((err) => {
|
||||
console.error(err);
|
||||
respond(500);
|
||||
});
|
||||
} else {
|
||||
respond(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { checkJSONPQuery, sendJSONResponse } from './json.js';
|
||||
|
||||
type CallbackResult = object | number;
|
||||
|
||||
/**
|
||||
* Handle JSON API response generated by a callback
|
||||
*/
|
||||
export function handleJSONResponse(
|
||||
req: FastifyRequest,
|
||||
res: FastifyReply,
|
||||
callback: (query: Record<string, string>) => CallbackResult | Promise<CallbackResult>
|
||||
) {
|
||||
const q = (req.query || {}) as Record<string, string>;
|
||||
|
||||
// Check for JSONP
|
||||
const wrap = checkJSONPQuery(q);
|
||||
if (!wrap) {
|
||||
// Invalid JSONP callback
|
||||
res.send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
// Function to send response
|
||||
const respond = (result: CallbackResult) => {
|
||||
if (typeof result === 'number') {
|
||||
res.send(result);
|
||||
} else {
|
||||
sendJSONResponse(result, q, wrap, res);
|
||||
}
|
||||
};
|
||||
|
||||
// Get result
|
||||
const result = callback(q);
|
||||
if (result instanceof Promise) {
|
||||
result.then(respond).catch((err) => {
|
||||
console.error(err);
|
||||
respond(500);
|
||||
});
|
||||
} else {
|
||||
respond(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,17 +3,18 @@ import fastifyFormBody from '@fastify/formbody';
|
|||
import { appConfig, httpHeaders } from '../config/app.js';
|
||||
import { runWhenLoaded } from '../data/loading.js';
|
||||
import { iconNameRoutePartialRegEx, iconNameRouteRegEx, splitIconName } from '../misc/name.js';
|
||||
import { generateAPIv1IconsListResponse } from './responses/collection-v1.js';
|
||||
import { generateAPIv2CollectionResponse } from './responses/collection-v2.js';
|
||||
import { generateCollectionsListResponse } from './responses/collections.js';
|
||||
import { generateIconsDataResponse } from './responses/icons.js';
|
||||
import { generateKeywordsResponse } from './responses/keywords.js';
|
||||
import { generateLastModifiedResponse } from './responses/modified.js';
|
||||
import { generateAPIv2SearchResponse } from './responses/search.js';
|
||||
import { createAPIv1IconsListResponse } from './responses/collection-v1.js';
|
||||
import { createAPIv2CollectionResponse } from './responses/collection-v2.js';
|
||||
import { createCollectionsListResponse } from './responses/collections.js';
|
||||
import { handleIconsDataResponse } from './helpers/send-icons.js';
|
||||
import { createKeywordsResponse } from './responses/keywords.js';
|
||||
import { createLastModifiedResponse } from './responses/modified.js';
|
||||
import { createAPIv2SearchResponse } from './responses/search.js';
|
||||
import { generateSVGResponse } from './responses/svg.js';
|
||||
import { generateUpdateResponse } from './responses/update.js';
|
||||
import { initVersionResponse, versionResponse } from './responses/version.js';
|
||||
import { generateIconsStyleResponse } from './responses/css.js';
|
||||
import { handleJSONResponse } from './helpers/send.js';
|
||||
|
||||
/**
|
||||
* Start HTTP server
|
||||
|
|
@ -88,12 +89,12 @@ export async function startHTTPServer() {
|
|||
// Icons data: /prefix/icons.json, /prefix.json
|
||||
server.get('/:prefix(' + iconNameRoutePartialRegEx + ')/icons.json', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateIconsDataResponse((req.params as PrefixParams).prefix, false, req.query, res);
|
||||
handleIconsDataResponse((req.params as PrefixParams).prefix, false, req.query, res);
|
||||
});
|
||||
});
|
||||
server.get('/:prefix(' + iconNameRoutePartialRegEx + ').json', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateIconsDataResponse((req.params as PrefixParams).prefix, false, req.query, res);
|
||||
handleIconsDataResponse((req.params as PrefixParams).prefix, false, req.query, res);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -107,19 +108,19 @@ export async function startHTTPServer() {
|
|||
// Icons data: /prefix/icons.js, /prefix.js
|
||||
server.get('/:prefix(' + iconNameRoutePartialRegEx + ')/icons.js', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateIconsDataResponse((req.params as PrefixParams).prefix, true, req.query, res);
|
||||
handleIconsDataResponse((req.params as PrefixParams).prefix, true, req.query, res);
|
||||
});
|
||||
});
|
||||
server.get('/:prefix(' + iconNameRoutePartialRegEx + ').js', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateIconsDataResponse((req.params as PrefixParams).prefix, true, req.query, res);
|
||||
handleIconsDataResponse((req.params as PrefixParams).prefix, true, req.query, res);
|
||||
});
|
||||
});
|
||||
|
||||
// Last modification time
|
||||
server.get('/last-modified', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateLastModifiedResponse(req.query, res);
|
||||
handleJSONResponse(req, res, createLastModifiedResponse);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -127,26 +128,26 @@ export async function startHTTPServer() {
|
|||
// Icon sets list
|
||||
server.get('/collections', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateCollectionsListResponse(req.query, res);
|
||||
handleJSONResponse(req, res, createCollectionsListResponse);
|
||||
});
|
||||
});
|
||||
|
||||
// Icons list, API v2
|
||||
server.get('/collection', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateAPIv2CollectionResponse(req.query, res);
|
||||
handleJSONResponse(req, res, createAPIv2CollectionResponse);
|
||||
});
|
||||
});
|
||||
|
||||
// Icons list, API v1
|
||||
server.get('/list-icons', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateAPIv1IconsListResponse(req.query, res, false);
|
||||
handleJSONResponse(req, res, (q) => createAPIv1IconsListResponse(q, false));
|
||||
});
|
||||
});
|
||||
server.get('/list-icons-categorized', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateAPIv1IconsListResponse(req.query, res, true);
|
||||
handleJSONResponse(req, res, (q) => createAPIv1IconsListResponse(q, true));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -154,14 +155,14 @@ export async function startHTTPServer() {
|
|||
// Search, currently version 2
|
||||
server.get('/search', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateAPIv2SearchResponse(req.query, res);
|
||||
handleJSONResponse(req, res, createAPIv2SearchResponse);
|
||||
});
|
||||
});
|
||||
|
||||
// Keywords
|
||||
server.get('/keywords', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateKeywordsResponse(req.query, res);
|
||||
handleJSONResponse(req, res, createKeywordsResponse);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { getPrefixes, iconSets } from '../../data/icon-sets.js';
|
||||
import type { IconSetAPIv2IconsList } from '../../types/icon-set/extra.js';
|
||||
import type { StoredIconSet } from '../../types/icon-set/storage.js';
|
||||
|
|
@ -7,11 +6,13 @@ import type {
|
|||
APIv1ListIconsCategorisedResponse,
|
||||
APIv1ListIconsResponse,
|
||||
} from '../../types/server/v1.js';
|
||||
import { checkJSONPQuery, sendJSONResponse } from '../helpers/json.js';
|
||||
import { filterPrefixesByPrefix } from '../helpers/prefixes.js';
|
||||
|
||||
// Response results, depends on `categorised` option
|
||||
type PossibleResults = APIv1ListIconsResponse | APIv1ListIconsCategorisedResponse;
|
||||
|
||||
/**
|
||||
* Send API v2 response
|
||||
* Create API v1 response
|
||||
*
|
||||
* This response ignores the following parameters:
|
||||
* - `aliases` -> always enabled
|
||||
|
|
@ -19,27 +20,15 @@ import { filterPrefixesByPrefix } from '../helpers/prefixes.js';
|
|||
*
|
||||
* Those parameters are always requested anyway, so does not make sense to re-create data in case they are disabled
|
||||
*/
|
||||
export function generateAPIv1IconsListResponse(
|
||||
query: FastifyRequest['query'],
|
||||
res: FastifyReply,
|
||||
export function createAPIv1IconsListResponse(
|
||||
query: Record<string, string>,
|
||||
categorised: boolean
|
||||
) {
|
||||
const q = (query || {}) as Record<string, string>;
|
||||
|
||||
const wrap = checkJSONPQuery(q);
|
||||
if (!wrap) {
|
||||
// Invalid JSONP callback
|
||||
res.send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
): PossibleResults | Record<string, PossibleResults> | number {
|
||||
function parse(
|
||||
prefix: string,
|
||||
iconSet: StoredIconSet,
|
||||
v2Cache: IconSetAPIv2IconsList
|
||||
): APIv1ListIconsResponse | APIv1ListIconsCategorisedResponse {
|
||||
const icons = iconSet.icons;
|
||||
|
||||
// Generate common data
|
||||
const base: APIv1ListIconsBaseResponse = {
|
||||
prefix,
|
||||
|
|
@ -48,13 +37,13 @@ export function generateAPIv1IconsListResponse(
|
|||
if (v2Cache.title) {
|
||||
base.title = v2Cache.title;
|
||||
}
|
||||
if (q.info && v2Cache.info) {
|
||||
if (query.info && v2Cache.info) {
|
||||
base.info = v2Cache.info;
|
||||
}
|
||||
if (q.aliases && v2Cache.aliases) {
|
||||
if (query.aliases && v2Cache.aliases) {
|
||||
base.aliases = v2Cache.aliases;
|
||||
}
|
||||
if (q.chars && v2Cache.chars) {
|
||||
if (query.chars && v2Cache.chars) {
|
||||
base.chars = v2Cache.chars;
|
||||
}
|
||||
|
||||
|
|
@ -81,22 +70,20 @@ export function generateAPIv1IconsListResponse(
|
|||
return result;
|
||||
}
|
||||
|
||||
if (q.prefix) {
|
||||
const prefix = q.prefix;
|
||||
if (query.prefix) {
|
||||
const prefix = query.prefix;
|
||||
const iconSet = iconSets[prefix]?.item;
|
||||
if (!iconSet || !iconSet.apiV2IconsCache) {
|
||||
res.send(404);
|
||||
return;
|
||||
return 404;
|
||||
}
|
||||
sendJSONResponse(parse(prefix, iconSet, iconSet.apiV2IconsCache), q, wrap, res);
|
||||
return;
|
||||
return parse(prefix, iconSet, iconSet.apiV2IconsCache);
|
||||
}
|
||||
|
||||
if (q.prefixes) {
|
||||
if (query.prefixes) {
|
||||
const prefixes = filterPrefixesByPrefix(
|
||||
getPrefixes(),
|
||||
{
|
||||
prefixes: q.prefixes,
|
||||
prefixes: query.prefixes,
|
||||
},
|
||||
false
|
||||
);
|
||||
|
|
@ -125,20 +112,18 @@ export function generateAPIv1IconsListResponse(
|
|||
|
||||
if (!items.length) {
|
||||
// Empty list
|
||||
res.send(404);
|
||||
return;
|
||||
return 404;
|
||||
}
|
||||
|
||||
// Get all items
|
||||
const result = Object.create(null) as Record<string, ReturnType<typeof parse>>;
|
||||
const result = Object.create(null) as Record<string, PossibleResults>;
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
result[item.prefix] = parse(item.prefix, item.iconSet, item.v2Cache);
|
||||
}
|
||||
sendJSONResponse(result, q, wrap, res);
|
||||
return;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Invalid
|
||||
res.send(400);
|
||||
return 400;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { iconSets } from '../../data/icon-sets.js';
|
||||
import type { APIv2CollectionResponse } from '../../types/server/v2.js';
|
||||
import { checkJSONPQuery, sendJSONResponse } from '../helpers/json.js';
|
||||
|
||||
/**
|
||||
* Send API v2 response
|
||||
|
|
@ -12,29 +10,18 @@ import { checkJSONPQuery, sendJSONResponse } from '../helpers/json.js';
|
|||
*
|
||||
* Those parameters are always requested anyway, so does not make sense to re-create data in case they are disabled
|
||||
*/
|
||||
export function generateAPIv2CollectionResponse(query: FastifyRequest['query'], res: FastifyReply) {
|
||||
const q = (query || {}) as Record<string, string>;
|
||||
|
||||
const wrap = checkJSONPQuery(q);
|
||||
if (!wrap) {
|
||||
// Invalid JSONP callback
|
||||
res.send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
export function createAPIv2CollectionResponse(q: Record<string, string>): APIv2CollectionResponse | number {
|
||||
// Get icon set
|
||||
const prefix = q.prefix;
|
||||
if (!prefix || !iconSets[prefix]) {
|
||||
res.send(404);
|
||||
return;
|
||||
return 404;
|
||||
}
|
||||
|
||||
const iconSet = iconSets[prefix].item;
|
||||
const apiV2IconsCache = iconSet.apiV2IconsCache;
|
||||
if (!apiV2IconsCache) {
|
||||
// Disabled
|
||||
res.send(404);
|
||||
return;
|
||||
return 404;
|
||||
}
|
||||
|
||||
// Generate response
|
||||
|
|
@ -52,5 +39,5 @@ export function generateAPIv2CollectionResponse(query: FastifyRequest['query'],
|
|||
delete response.chars;
|
||||
}
|
||||
|
||||
sendJSONResponse(response, q, wrap, res);
|
||||
return response;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { getPrefixes, iconSets } from '../../data/icon-sets.js';
|
||||
import type { APIv2CollectionsResponse } from '../../types/server/v2.js';
|
||||
import { checkJSONPQuery, sendJSONResponse } from '../helpers/json.js';
|
||||
import { filterPrefixesByPrefix } from '../helpers/prefixes.js';
|
||||
|
||||
/**
|
||||
|
|
@ -12,15 +10,7 @@ import { filterPrefixesByPrefix } from '../helpers/prefixes.js';
|
|||
* Ignored parameters:
|
||||
* - hidden (always enabled)
|
||||
*/
|
||||
export function generateCollectionsListResponse(query: FastifyRequest['query'], res: FastifyReply) {
|
||||
const q = (query || {}) as Record<string, string>;
|
||||
const wrap = checkJSONPQuery(q);
|
||||
if (!wrap) {
|
||||
// Invalid JSONP callback
|
||||
res.send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
export function createCollectionsListResponse(q: Record<string, string>): APIv2CollectionsResponse {
|
||||
// Filter prefixes
|
||||
const prefixes = filterPrefixesByPrefix(getPrefixes('info'), q, false);
|
||||
const response = Object.create(null) as APIv2CollectionsResponse;
|
||||
|
|
@ -33,5 +23,5 @@ export function generateCollectionsListResponse(query: FastifyRequest['query'],
|
|||
}
|
||||
}
|
||||
|
||||
sendJSONResponse(response, q, wrap, res);
|
||||
return response;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,45 +1,59 @@
|
|||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { IconifyJSON } from '@iconify/types';
|
||||
import { getStoredIconsData } from '../../data/icon-set/utils/get-icons.js';
|
||||
import { iconSets } from '../../data/icon-sets.js';
|
||||
import { checkJSONPQuery, sendJSONResponse } from '../helpers/json.js';
|
||||
|
||||
/**
|
||||
* Generate icons data
|
||||
*/
|
||||
export function generateIconsDataResponse(
|
||||
export function createIconsDataResponse(
|
||||
prefix: string,
|
||||
wrapJS: boolean,
|
||||
query: FastifyRequest['query'],
|
||||
res: FastifyReply
|
||||
) {
|
||||
const q = (query || {}) as Record<string, string>;
|
||||
q: Record<string, string>
|
||||
): number | IconifyJSON | Promise<IconifyJSON | number> {
|
||||
const names = q.icons?.split(',');
|
||||
|
||||
if (!names || !names.length) {
|
||||
// Missing or invalid icons parameter
|
||||
res.send(404);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for JSONP
|
||||
const wrap = checkJSONPQuery(q, wrapJS, 'SimpleSVG._loaderCallback');
|
||||
if (!wrap) {
|
||||
// Invalid JSONP callback
|
||||
res.send(400);
|
||||
return;
|
||||
return 404;
|
||||
}
|
||||
|
||||
// Get icon set
|
||||
const iconSet = iconSets[prefix];
|
||||
if (!iconSet) {
|
||||
// No such icon set
|
||||
res.send(404);
|
||||
return;
|
||||
return 404;
|
||||
}
|
||||
|
||||
// Get icons
|
||||
// Get icons, possibly sync
|
||||
let syncData: IconifyJSON | undefined;
|
||||
let resolveData: undefined | ((data: IconifyJSON) => void);
|
||||
|
||||
getStoredIconsData(iconSet.item, names, (data) => {
|
||||
// Send data
|
||||
sendJSONResponse(data, q, wrap, res);
|
||||
if (resolveData) {
|
||||
resolveData(data);
|
||||
} else {
|
||||
syncData = data;
|
||||
}
|
||||
});
|
||||
|
||||
if (syncData) {
|
||||
return syncData;
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
resolveData = resolve;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Awaitable version of createIconsDataResponse()
|
||||
*/
|
||||
export function createIconsDataResponseAsync(prefix: string, q: Record<string, string>): Promise<IconifyJSON | number> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const result = createIconsDataResponse(prefix, q);
|
||||
if (result instanceof Promise) {
|
||||
result.then(resolve).catch(reject);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,16 @@
|
|||
import { matchIconName } from '@iconify/utils/lib/icon/name';
|
||||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { searchIndex } from '../../data/search.js';
|
||||
import { getPartialKeywords } from '../../data/search/partial.js';
|
||||
import type { APIv3KeywordsQuery, APIv3KeywordsResponse } from '../../types/server/keywords.js';
|
||||
import { checkJSONPQuery, sendJSONResponse } from '../helpers/json.js';
|
||||
|
||||
/**
|
||||
* Generate icons data
|
||||
* Find full keywords for partial keyword
|
||||
*/
|
||||
export function generateKeywordsResponse(query: FastifyRequest['query'], res: FastifyReply) {
|
||||
const q = (query || {}) as Record<string, string>;
|
||||
const wrap = checkJSONPQuery(q);
|
||||
if (!wrap) {
|
||||
// Invalid JSONP callback
|
||||
res.send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
export function createKeywordsResponse(q: Record<string, string>): number | APIv3KeywordsResponse {
|
||||
// Check if search data is available
|
||||
const searchIndexData = searchIndex.data;
|
||||
if (!searchIndexData) {
|
||||
res.send(404);
|
||||
return;
|
||||
return 404;
|
||||
}
|
||||
const keywords = searchIndexData.keywords;
|
||||
|
||||
|
|
@ -32,15 +21,16 @@ export function generateKeywordsResponse(query: FastifyRequest['query'], res: Fa
|
|||
let failed = false;
|
||||
|
||||
if (typeof q.prefix === 'string') {
|
||||
// Keywords should start with prefix
|
||||
test = q.prefix;
|
||||
suffixes = false;
|
||||
} else if (typeof q.keyword === 'string') {
|
||||
// All keywords that contain keyword
|
||||
test = q.keyword;
|
||||
suffixes = true;
|
||||
} else {
|
||||
// Invalid query
|
||||
res.send(400);
|
||||
return;
|
||||
return 400;
|
||||
}
|
||||
test = test.toLowerCase().trim();
|
||||
|
||||
|
|
@ -71,5 +61,5 @@ export function generateKeywordsResponse(query: FastifyRequest['query'], res: Fa
|
|||
matches: failed || invalid ? [] : getPartialKeywords(test, suffixes, searchIndexData)?.slice(0) || [],
|
||||
};
|
||||
|
||||
sendJSONResponse(response, q, wrap, res);
|
||||
return response;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,11 @@
|
|||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { getPrefixes, iconSets } from '../../data/icon-sets.js';
|
||||
import type { APIv3LastModifiedResponse } from '../../types/server/modified.js';
|
||||
import { checkJSONPQuery, sendJSONResponse } from '../helpers/json.js';
|
||||
import { filterPrefixesByPrefix } from '../helpers/prefixes.js';
|
||||
|
||||
/**
|
||||
* Generate icons data
|
||||
* Get last modified time for all icon sets
|
||||
*/
|
||||
export function generateLastModifiedResponse(query: FastifyRequest['query'], res: FastifyReply) {
|
||||
const q = (query || {}) as Record<string, string>;
|
||||
const wrap = checkJSONPQuery(q);
|
||||
if (!wrap) {
|
||||
// Invalid JSONP callback
|
||||
res.send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
export function createLastModifiedResponse(q: Record<string, string>): number | APIv3LastModifiedResponse {
|
||||
// Filter prefixes
|
||||
const prefixes = filterPrefixesByPrefix(getPrefixes(), q, false);
|
||||
|
||||
|
|
@ -36,5 +26,5 @@ export function generateLastModifiedResponse(query: FastifyRequest['query'], res
|
|||
}
|
||||
}
|
||||
|
||||
sendJSONResponse(response, q, wrap, res);
|
||||
return response;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,28 +14,17 @@ const defaultSearchLimit = minSearchLimit * 2;
|
|||
/**
|
||||
* Send API v2 response
|
||||
*/
|
||||
export function generateAPIv2SearchResponse(query: FastifyRequest['query'], res: FastifyReply) {
|
||||
const q = (query || {}) as Record<string, string>;
|
||||
|
||||
const wrap = checkJSONPQuery(q);
|
||||
if (!wrap) {
|
||||
// Invalid JSONP callback
|
||||
res.send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
export function createAPIv2SearchResponse(q: Record<string, string>): number | APIv2SearchResponse {
|
||||
// Check if search data is available
|
||||
const searchIndexData = searchIndex.data;
|
||||
if (!searchIndexData) {
|
||||
res.send(404);
|
||||
return;
|
||||
return 404;
|
||||
}
|
||||
|
||||
// Get query
|
||||
const keyword = q.query;
|
||||
if (!keyword) {
|
||||
res.send(400);
|
||||
return;
|
||||
return 400;
|
||||
}
|
||||
|
||||
// Convert to params
|
||||
|
|
@ -49,16 +38,14 @@ export function generateAPIv2SearchResponse(query: FastifyRequest['query'], res:
|
|||
if (v2Query.limit) {
|
||||
const limit = parseInt(v2Query.limit);
|
||||
if (!limit) {
|
||||
res.send(400);
|
||||
return;
|
||||
return 400;
|
||||
}
|
||||
params.limit = Math.max(minSearchLimit, Math.min(limit, maxSearchLimit));
|
||||
}
|
||||
if (v2Query.min) {
|
||||
const limit = parseInt(v2Query.min);
|
||||
if (!limit) {
|
||||
res.send(400);
|
||||
return;
|
||||
return 400;
|
||||
}
|
||||
params.limit = Math.max(minSearchLimit, Math.min(limit, maxSearchLimit));
|
||||
params.softLimit = true;
|
||||
|
|
@ -68,8 +55,7 @@ export function generateAPIv2SearchResponse(query: FastifyRequest['query'], res:
|
|||
if (v2Query.start) {
|
||||
start = parseInt(v2Query.start);
|
||||
if (isNaN(start) || start < 0 || start >= params.limit) {
|
||||
res.send(400);
|
||||
return;
|
||||
return 400;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -130,5 +116,5 @@ export function generateAPIv2SearchResponse(query: FastifyRequest['query'], res:
|
|||
};
|
||||
}
|
||||
|
||||
sendJSONResponse(response, q, wrap, res);
|
||||
return response;
|
||||
}
|
||||
|
|
|
|||
17
src/index.ts
17
src/index.ts
|
|
@ -1,30 +1,19 @@
|
|||
import { config } from 'dotenv';
|
||||
import { getImporters } from './config/icon-sets.js';
|
||||
import { iconSetsStorage } from './data/icon-set/store/storage.js';
|
||||
import { setImporters, updateIconSets } from './data/icon-sets.js';
|
||||
import { loaded } from './data/loading.js';
|
||||
import { cleanupStorageCache } from './data/storage/startup.js';
|
||||
import { startHTTPServer } from './http/index.js';
|
||||
import { loadEnvConfig } from './misc/load-config.js';
|
||||
import { initAPI } from './init.js';
|
||||
|
||||
(async () => {
|
||||
// Configure environment
|
||||
config();
|
||||
loadEnvConfig();
|
||||
|
||||
// Reset old cache
|
||||
await cleanupStorageCache(iconSetsStorage);
|
||||
|
||||
// Start HTTP server
|
||||
startHTTPServer();
|
||||
|
||||
// Get all importers and load data
|
||||
const importers = await getImporters();
|
||||
for (let i = 0; i < importers.length; i++) {
|
||||
await importers[i].init();
|
||||
}
|
||||
setImporters(importers);
|
||||
updateIconSets();
|
||||
// Init API
|
||||
await initAPI();
|
||||
|
||||
// Loaded
|
||||
loaded();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
import { getImporters } from './config/icon-sets.js';
|
||||
import { iconSetsStorage } from './data/icon-set/store/storage.js';
|
||||
import { setImporters, updateIconSets } from './data/icon-sets.js';
|
||||
import { cleanupStorageCache } from './data/storage/startup.js';
|
||||
|
||||
/**
|
||||
* Init API
|
||||
*/
|
||||
export async function initAPI() {
|
||||
// Reset old cache
|
||||
await cleanupStorageCache(iconSetsStorage);
|
||||
|
||||
// Get all importers and load data
|
||||
const importers = await getImporters();
|
||||
for (let i = 0; i < importers.length; i++) {
|
||||
await importers[i].init();
|
||||
}
|
||||
setImporters(importers);
|
||||
updateIconSets();
|
||||
}
|
||||
Loading…
Reference in New Issue