Compare commits

...

2 Commits

Author SHA1 Message Date
ae7f05786d upgrade APIlite and change build backend use TypeScript instead of JavaScript 2026-02-13 10:40:32 +01:00
d13d861113 new favicon from logo 2026-02-13 08:07:21 +01:00
7 changed files with 211 additions and 166 deletions

7
backend/composer.lock generated
View File

@ -8,11 +8,11 @@
"packages": [ "packages": [
{ {
"name": "tpsoft/apilite", "name": "tpsoft/apilite",
"version": "v1.1.0", "version": "v1.2.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://gitea.tpsoft.org/TPsoft.org/APIlite.git", "url": "https://gitea.tpsoft.org/TPsoft.org/APIlite.git",
"reference": "fab8efd780ede046ced076f237351cdba5a8a51f" "reference": "d258bcc91948424711dd79fde57254e1384d0091"
}, },
"require": { "require": {
"php": ">=8.2" "php": ">=8.2"
@ -41,6 +41,7 @@
"description": "A set of tools to simplify the work of creating backend APIs for your frontend projects.", "description": "A set of tools to simplify the work of creating backend APIs for your frontend projects.",
"keywords": [ "keywords": [
"api", "api",
"javascript",
"json", "json",
"php", "php",
"rest", "rest",
@ -52,7 +53,7 @@
"type": "other" "type": "other"
} }
], ],
"time": "2026-02-09T06:33:17+00:00" "time": "2026-02-13T08:54:08+00:00"
}, },
{ {
"name": "tpsoft/dbmodel", "name": "tpsoft/dbmodel",

View File

@ -9,7 +9,7 @@ $backend_api = new TPsoft\Nutrio\API('typescript', 'import.meta.env.VITE_BACKEND
$output = ob_get_contents(); $output = ob_get_contents();
ob_end_clean(); ob_end_clean();
$ts_path = realpath(__DIR__ . '/../../frontend/src').'/BackendAPI.js'; $ts_path = realpath(__DIR__ . '/../../frontend/src').'/BackendAPI.ts';
$suc = file_put_contents($ts_path, $output); $suc = file_put_contents($ts_path, $output);
if ($suc === false) { if ($suc === false) {
echo "✗ TypeScript store into file failed\n"; echo "✗ TypeScript store into file failed\n";

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 121 KiB

View File

@ -1,161 +0,0 @@
/**
* Generated by APIlite
* https://gitea.tpsoft.org/TPsoft.org/APIlite
*
* 2026-02-13 06:55:45 */
class BackendAPI {
endpoint = import.meta.env.VITE_BACKENDAPI_URL;
/* ----------------------------------------------------
* General API call
*/
call(method, data, callback) {
var xhttp = new XMLHttpRequest();
xhttp.withCredentials = true;
xhttp.onreadystatechange = function() {
if (this.readyState == 4) {
if (this.status == 200) {
if (callback != null) callback(JSON.parse(this.responseText));
} else {
if (callback != null) callback({'status': 'ERROR', 'message': 'HTTP STATUS ' + this.status});
}
}
}
var form_data = new FormData();
Object.keys(data).forEach(key => {
let val = data[key];
if (typeof val == 'object') val = JSON.stringify(val);
form_data.append(key, val);
});
xhttp.open('POST', this.endpoint + '?action=' + method);
xhttp.send(form_data);
}
callPromise(method, data) {
return new Promise((resolve, reject) => {
this.call(method, data, function(response) {
if (method == '__HELP__') {
resolve(response);
return;
}
if (response.status == 'OK') {
resolve(response.data);
} else {
reject(response.msg);
}
});
})
}
/* ----------------------------------------------------
* API actions
*/
help() {
return this.callPromise('__HELP__', {});
}
health() {
return this.callPromise('health', {});
}
userRegistration(email, password) {
return this.callPromise('userRegistration', {email: email, password: password});
}
userLogin(email, password) {
return this.callPromise('userLogin', {email: email, password: password});
}
userDelete(email, password) {
return this.callPromise('userDelete', {email: email, password: password});
}
userLogout(token) {
return this.callPromise('userLogout', {token: token});
}
ingredientList(token, query, include_global) {
return this.callPromise('ingredientList', {token: token, query: query, include_global: include_global});
}
ingredientGet(token, ingredient_id) {
return this.callPromise('ingredientGet', {token: token, ingredient_id: ingredient_id});
}
ingredientCreate(token, name, protein_g_100, carbs_g_100, sugar_g_100, fat_g_100, fiber_g_100, kcal_100) {
return this.callPromise('ingredientCreate', {token: token, name: name, protein_g_100: protein_g_100, carbs_g_100: carbs_g_100, sugar_g_100: sugar_g_100, fat_g_100: fat_g_100, fiber_g_100: fiber_g_100, kcal_100: kcal_100});
}
ingredientUpdate(token, ingredient_id, name, protein_g_100, carbs_g_100, sugar_g_100, fat_g_100, fiber_g_100, kcal_100) {
return this.callPromise('ingredientUpdate', {token: token, ingredient_id: ingredient_id, name: name, protein_g_100: protein_g_100, carbs_g_100: carbs_g_100, sugar_g_100: sugar_g_100, fat_g_100: fat_g_100, fiber_g_100: fiber_g_100, kcal_100: kcal_100});
}
ingredientDelete(token, ingredient_id) {
return this.callPromise('ingredientDelete', {token: token, ingredient_id: ingredient_id});
}
mealList(token, meal_type, with_items, with_totals) {
return this.callPromise('mealList', {token: token, meal_type: meal_type, with_items: with_items, with_totals: with_totals});
}
mealGet(token, meal_id, with_items, with_totals) {
return this.callPromise('mealGet', {token: token, meal_id: meal_id, with_items: with_items, with_totals: with_totals});
}
mealCreate(token, name, meal_type) {
return this.callPromise('mealCreate', {token: token, name: name, meal_type: meal_type});
}
mealUpdate(token, meal_id, name, meal_type) {
return this.callPromise('mealUpdate', {token: token, meal_id: meal_id, name: name, meal_type: meal_type});
}
mealDelete(token, meal_id) {
return this.callPromise('mealDelete', {token: token, meal_id: meal_id});
}
mealItemList(token, meal_id, with_calculated) {
return this.callPromise('mealItemList', {token: token, meal_id: meal_id, with_calculated: with_calculated});
}
mealItemAdd(token, meal_id, ingredient_id, grams, position) {
return this.callPromise('mealItemAdd', {token: token, meal_id: meal_id, ingredient_id: ingredient_id, grams: grams, position: position});
}
mealItemUpdate(token, meal_item_id, ingredient_id, grams, position) {
return this.callPromise('mealItemUpdate', {token: token, meal_item_id: meal_item_id, ingredient_id: ingredient_id, grams: grams, position: position});
}
mealItemDelete(token, meal_item_id) {
return this.callPromise('mealItemDelete', {token: token, meal_item_id: meal_item_id});
}
mealItemReorder(token, meal_id, ordered_item_ids) {
return this.callPromise('mealItemReorder', {token: token, meal_id: meal_id, ordered_item_ids: ordered_item_ids});
}
mealTotals(token, meal_id) {
return this.callPromise('mealTotals', {token: token, meal_id: meal_id});
}
diaryDayGet(token, day_date, with_totals) {
return this.callPromise('diaryDayGet', {token: token, day_date: day_date, with_totals: with_totals});
}
diaryDaySetMeal(token, day_date, meal_type, meal_id) {
return this.callPromise('diaryDaySetMeal', {token: token, day_date: day_date, meal_type: meal_type, meal_id: meal_id});
}
diaryDayUnsetMeal(token, day_date, meal_type) {
return this.callPromise('diaryDayUnsetMeal', {token: token, day_date: day_date, meal_type: meal_type});
}
diaryRange(token, date_from, date_to) {
return this.callPromise('diaryRange', {token: token, date_from: date_from, date_to: date_to});
}
};
export default new BackendAPI();

205
frontend/src/BackendAPI.ts Normal file
View File

@ -0,0 +1,205 @@
/**
* Generated by APIlite
* https://gitea.tpsoft.org/TPsoft.org/APIlite
*
* 2026-02-13 10:00:58 */
export interface APIliteActionResponse<T> {
status: 'OK';
data: T;
}
export interface APIliteErrorResponse {
status: 'ERROR';
msg: string;
}
export interface APIliteMethodParam {
name: string;
type: string | null;
optional: boolean;
default: unknown;
doc: string | null;
}
export interface APIliteMethodDoc {
name: string;
doc: string | null;
description: string | null;
params: APIliteMethodParam[];
return: string | string[] | null;
}
export interface APIliteHelpResponse {
name: string;
html_version: string;
javascript_version: string;
typescript_version: string;
actions: APIliteMethodDoc[];
}
class BackendAPI {
endpoint: string = import.meta.env.VITE_BACKENDAPI_URL;
private call(
method: string,
data: Record<string, unknown>,
callback: (response: APIliteHelpResponse | APIliteActionResponse<unknown> | APIliteErrorResponse) => void
): void {
const xhttp = new XMLHttpRequest();
xhttp.withCredentials = true;
xhttp.onreadystatechange = function() {
if (this.readyState === 4) {
if (this.status === 200) {
const response = JSON.parse(this.responseText) as APIliteHelpResponse | APIliteActionResponse<unknown> | APIliteErrorResponse;
callback(response);
} else {
callback({ status: 'ERROR', msg: 'HTTP STATUS ' + this.status });
}
}
};
const formData = new FormData();
Object.keys(data).forEach((key) => {
const rawValue = data[key];
if (typeof rawValue === 'undefined') {
return;
}
let value: string | Blob;
if (rawValue instanceof Blob) {
value = rawValue;
} else if (typeof rawValue === 'object' && rawValue !== null) {
value = JSON.stringify(rawValue);
} else {
value = String(rawValue);
}
formData.append(key, value);
});
xhttp.open('POST', this.endpoint + '?action=' + method);
xhttp.send(formData);
}
private callPromise<T>(method: string, data: Record<string, unknown>): Promise<T> {
return new Promise<T>((resolve, reject) => {
this.call(method, data, (response) => {
if (method === '__HELP__') {
resolve(response as T);
return;
}
if (response.status === 'OK') {
resolve(response.data as T);
return;
}
reject(response.msg);
});
});
}
help(): Promise<APIliteHelpResponse> {
return this.callPromise<APIliteHelpResponse>('__HELP__', {});
}
health(): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('health', { });
}
userRegistration(email: string, password: string): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('userRegistration', { email, password });
}
userLogin(email: string, password: string): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('userLogin', { email, password });
}
userDelete(email: string, password: string): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('userDelete', { email, password });
}
userLogout(token: string): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('userLogout', { token });
}
ingredientList(token: string, query?: string, include_global?: boolean): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('ingredientList', { token, query, include_global });
}
ingredientGet(token: string, ingredient_id: number): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('ingredientGet', { token, ingredient_id });
}
ingredientCreate(token: string, name: string, protein_g_100: number, carbs_g_100: number, sugar_g_100: number, fat_g_100: number, fiber_g_100?: number, kcal_100?: number): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('ingredientCreate', { token, name, protein_g_100, carbs_g_100, sugar_g_100, fat_g_100, fiber_g_100, kcal_100 });
}
ingredientUpdate(token: string, ingredient_id: number, name: string, protein_g_100: number, carbs_g_100: number, sugar_g_100: number, fat_g_100: number, fiber_g_100?: number, kcal_100?: number): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('ingredientUpdate', { token, ingredient_id, name, protein_g_100, carbs_g_100, sugar_g_100, fat_g_100, fiber_g_100, kcal_100 });
}
ingredientDelete(token: string, ingredient_id: number): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('ingredientDelete', { token, ingredient_id });
}
mealList(token: string, meal_type?: string, with_items?: boolean, with_totals?: boolean): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('mealList', { token, meal_type, with_items, with_totals });
}
mealGet(token: string, meal_id: number, with_items?: boolean, with_totals?: boolean): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('mealGet', { token, meal_id, with_items, with_totals });
}
mealCreate(token: string, name: string, meal_type: string): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('mealCreate', { token, name, meal_type });
}
mealUpdate(token: string, meal_id: number, name: string, meal_type: string): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('mealUpdate', { token, meal_id, name, meal_type });
}
mealDelete(token: string, meal_id: number): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('mealDelete', { token, meal_id });
}
mealItemList(token: string, meal_id: number, with_calculated?: boolean): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('mealItemList', { token, meal_id, with_calculated });
}
mealItemAdd(token: string, meal_id: number, ingredient_id: number, grams: number, position?: number): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('mealItemAdd', { token, meal_id, ingredient_id, grams, position });
}
mealItemUpdate(token: string, meal_item_id: number, ingredient_id: number, grams: number, position: number): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('mealItemUpdate', { token, meal_item_id, ingredient_id, grams, position });
}
mealItemDelete(token: string, meal_item_id: number): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('mealItemDelete', { token, meal_item_id });
}
mealItemReorder(token: string, meal_id: number, ordered_item_ids: unknown[]): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('mealItemReorder', { token, meal_id, ordered_item_ids });
}
mealTotals(token: string, meal_id: number): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('mealTotals', { token, meal_id });
}
diaryDayGet(token: string, day_date: string, with_totals?: boolean): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('diaryDayGet', { token, day_date, with_totals });
}
diaryDaySetMeal(token: string, day_date: string, meal_type: string, meal_id: number): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('diaryDaySetMeal', { token, day_date, meal_type, meal_id });
}
diaryDayUnsetMeal(token: string, day_date: string, meal_type: string): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('diaryDayUnsetMeal', { token, day_date, meal_type });
}
diaryRange(token: string, date_from: string, date_to: string): Promise<APIliteActionResponse<unknown[]>> {
return this.callPromise<APIliteActionResponse<unknown[]>>('diaryRange', { token, date_from, date_to });
}
}
export default new BackendAPI();

View File

@ -12,7 +12,7 @@ import {
faSun, faSun,
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import BackendAPI from '@/BackendAPI.js' import BackendAPI from '@/BackendAPI.ts'
import i18n from '@/i18n' import i18n from '@/i18n'
import { SUPPORTED_LOCALES, type AppLocale } from '@/i18n' import { SUPPORTED_LOCALES, type AppLocale } from '@/i18n'