dev: add icon_size query for get-launch-apps

This commit is contained in:
KernelDeimos 2024-12-20 15:08:10 -05:00
parent f14e1fefcf
commit df10a7c332
3 changed files with 79 additions and 15 deletions

View File

@ -22,6 +22,41 @@ const router = express.Router();
const auth = require('../middleware/auth.js'); const auth = require('../middleware/auth.js');
const { get_app } = require('../helpers.js'); const { get_app } = require('../helpers.js');
const { DB_READ } = require('../services/database/consts.js'); const { DB_READ } = require('../services/database/consts.js');
const { stream_to_buffer } = require('../util/streamutil.js');
const get_apps = async ({ specifiers }) => {
return await Promise.all(specifiers.map(async (specifier) => {
return await get_app(specifier);
}));
};
const iconify_apps = async (context, { apps, size }) => {
return await Promise.all(apps.map(async app => {
const data_url = app.icon;
if ( ! data_url ) return app;
const metadata = data_url.split(',')[0];
const input_mime = metadata.split(';')[0].split(':')[1];
// svg icons will be sent as-is
if (input_mime === 'image/svg+xml') {
return app;
}
const svc_appIcon = context.services.get('app-icon');
const { stream, mime } = await svc_appIcon.get_icon_stream({
app_icon: app.icon,
app_uid: app.uid ?? app.uuid,
size: size,
});
const buffer = await stream_to_buffer(stream);
const resp_data_url = `data:image/png;base64,${buffer.toString('base64')}`;
app.icon = resp_data_url;
return app;
}));
}
// -----------------------------------------------------------------------// // -----------------------------------------------------------------------//
// GET /get-launch-apps // GET /get-launch-apps
@ -29,10 +64,22 @@ const { DB_READ } = require('../services/database/consts.js');
module.exports = async (req, res) => { module.exports = async (req, res) => {
let result = {}; let result = {};
// Verify query params
if ( req.query.icon_size ) {
const ALLOWED_SIZES = ['16', '32', '64', '128', '256', '512'];
if ( ! ALLOWED_SIZES.includes(req.query.icon_size) ) {
res.status(400).send({ error: 'Invalid icon_size' });
}
}
// -----------------------------------------------------------------------// // -----------------------------------------------------------------------//
// Recommended apps // Recommended apps
// -----------------------------------------------------------------------// // -----------------------------------------------------------------------//
result.recommended = kv.get('global:recommended-apps'); const recommended_cache_key = 'global:recommended-apps' + (
req.query.icon_size ? `:icon-size:${req.query.icon_size}` : ''
);
result.recommended = kv.get(recommended_cache_key);
if ( ! result.recommended ) { if ( ! result.recommended ) {
let app_names = new Set([ let app_names = new Set([
'app-center', 'app-center',
@ -62,12 +109,10 @@ module.exports = async (req, res) => {
// Prepare each app for returning to user by only returning the necessary fields // Prepare each app for returning to user by only returning the necessary fields
// and adding them to the retobj array // and adding them to the retobj array
result.recommended = []; result.recommended = (await get_apps({
for ( const name of app_names ) { specifiers: Array.from(app_names).map(name => ({ name }))
const app = await get_app({ name }); })).filter(app => !! app).map(app => {
if ( ! app ) continue; return {
result.recommended.push({
uuid: app.uid, uuid: app.uid,
name: app.name, name: app.name,
title: app.title, title: app.title,
@ -75,10 +120,18 @@ module.exports = async (req, res) => {
godmode: app.godmode, godmode: app.godmode,
maximize_on_start: app.maximize_on_start, maximize_on_start: app.maximize_on_start,
index_url: app.index_url, index_url: app.index_url,
};
});
// Iconify apps
if ( req.query.icon_size ) {
result.recommended = await iconify_apps({ services: req.services }, {
apps: result.recommended,
size: req.query.icon_size,
}); });
} }
kv.set('global:recommended-apps', result.recommended); kv.set(recommended_cache_key, result.recommended);
} }
// -----------------------------------------------------------------------// // -----------------------------------------------------------------------//
@ -122,5 +175,13 @@ module.exports = async (req, res) => {
}); });
} }
// Iconify apps
if ( req.query.icon_size ) {
result.recent = await iconify_apps({ services: req.services }, {
apps: result.recent,
size: req.query.icon_size,
});
}
return res.send(result); return res.send(result);
}; };

View File

@ -124,6 +124,7 @@ describe('GET /launch-apps', () => {
// First call // First call
{ {
const { get_launch_apps, req_mock, res_mock, spies } = get_mock_context(); const { get_launch_apps, req_mock, res_mock, spies } = get_mock_context();
req_mock.query = {};
await get_launch_apps(req_mock, res_mock); await get_launch_apps(req_mock, res_mock);
expect(res_mock.send.calledOnce).to.equal(true, 'res.send should be called once'); expect(res_mock.send.calledOnce).to.equal(true, 'res.send should be called once');
@ -177,6 +178,7 @@ describe('GET /launch-apps', () => {
// Second call // Second call
{ {
const { get_launch_apps, req_mock, res_mock, spies } = get_mock_context(); const { get_launch_apps, req_mock, res_mock, spies } = get_mock_context();
req_mock.query = {};
await get_launch_apps(req_mock, res_mock); await get_launch_apps(req_mock, res_mock);
expect(res_mock.send.calledOnce).to.equal(true, 'res.send should be called once'); expect(res_mock.send.calledOnce).to.equal(true, 'res.send should be called once');

View File

@ -62,20 +62,21 @@ class AppIconService extends BaseService {
}).attach(app); }).attach(app);
} }
async get_icon_stream ({ app_uid, size }) { async get_icon_stream ({ app_icon, app_uid, size }) {
// Get icon file node // Get icon file node
const dir_app_icons = await this.get_app_icons(); const dir_app_icons = await this.get_app_icons();
console.log('APP UID', app_uid);
const node = await dir_app_icons.getChild(`${app_uid}-${size}.png`); const node = await dir_app_icons.getChild(`${app_uid}-${size}.png`);
if ( ! await node.exists() ) { if ( ! await node.exists() ) {
// Use database-stored icon as a fallback // Use database-stored icon as a fallback
app_icon = app_icon ?? await (async () => {
const app = await get_app({ uid: app_uid }); const app = await get_app({ uid: app_uid });
if ( ! app.icon ) { return app.icon ?? DEFAULT_APP_ICON;
app.icon = DEFAULT_APP_ICON; })()
} const [metadata, base64] = app_icon.split(',');
const [metadata, app_icon] = app.icon.split(',');
console.log('METADATA', metadata); console.log('METADATA', metadata);
const mime = metadata.split(';')[0].split(':')[1]; const mime = metadata.split(';')[0].split(':')[1];
const img = Buffer.from(app_icon, 'base64'); const img = Buffer.from(base64, 'base64');
return { return {
mime, mime,
stream: buffer_to_stream(img), stream: buffer_to_stream(img),