build: add TypeScript configuration and generate declaration files

- Add tsconfig.json for TypeScript compilation with declaration and source map generation
- Generate .d.ts declaration files for all modules, services, controllers, and models
- Update package.json with NestJS dependencies and TypeScript development tools
- Include database files in the distribution output for persistence
This commit is contained in:
Jp
2026-01-31 09:00:26 +08:00
parent 0fa0343798
commit f521970a65
174 changed files with 7205 additions and 1633 deletions

32
dist/meals/meals.controller.d.ts vendored Normal file
View File

@@ -0,0 +1,32 @@
import { Response } from 'express';
import { UtilsService } from '../utils/utils.service';
import { NutritionService } from './nutrition.service';
import { Meal } from '../models/meal.model';
import { MealFood } from '../models/meal-food.model';
import { FoodItem } from '../models/food-item.model';
import { WaterLog } from '../models/water-log.model';
import { WeightLog } from '../models/weight-log.model';
export declare class MealsController {
private utilsService;
private nutritionService;
private mealModel;
private mealFoodModel;
private foodItemModel;
private waterLogModel;
private weightLogModel;
constructor(utilsService: UtilsService, nutritionService: NutritionService, mealModel: typeof Meal, mealFoodModel: typeof MealFood, foodItemModel: typeof FoodItem, waterLogModel: typeof WaterLog, weightLogModel: typeof WeightLog);
addMealPage(req: any, res: Response): void;
addMeal(req: any, res: Response, body: any): Promise<void>;
searchFood(query: string): Promise<any[]>;
addFood(body: any): Promise<{
success: boolean;
food_id: any;
name: string;
} | {
success: boolean;
food_id?: undefined;
name?: undefined;
}>;
addWater(req: any, res: Response, body: any): Promise<void>;
addWeight(req: any, res: Response, body: any): Promise<void>;
}

220
dist/meals/meals.controller.js vendored Normal file
View File

@@ -0,0 +1,220 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MealsController = void 0;
const common_1 = require("@nestjs/common");
const sequelize_1 = require("@nestjs/sequelize");
const authenticated_guard_1 = require("../auth/guards/authenticated.guard");
const utils_service_1 = require("../utils/utils.service");
const nutrition_service_1 = require("./nutrition.service");
const meal_model_1 = require("../models/meal.model");
const meal_food_model_1 = require("../models/meal-food.model");
const food_item_model_1 = require("../models/food-item.model");
const water_log_model_1 = require("../models/water-log.model");
const weight_log_model_1 = require("../models/weight-log.model");
let MealsController = class MealsController {
constructor(utilsService, nutritionService, mealModel, mealFoodModel, foodItemModel, waterLogModel, weightLogModel) {
this.utilsService = utilsService;
this.nutritionService = nutritionService;
this.mealModel = mealModel;
this.mealFoodModel = mealFoodModel;
this.foodItemModel = foodItemModel;
this.waterLogModel = waterLogModel;
this.weightLogModel = weightLogModel;
}
addMealPage(req, res) {
res.render('add_meal', { today: new Date().toISOString().split('T')[0] });
}
async addMeal(req, res, body) {
try {
let { date, meal_type, time, 'food_id[]': foodIds, 'quantity[]': quantities } = body;
if (!foodIds) {
req.flash('error_msg', 'Please add at least one food item');
return res.redirect('/add-meal');
}
if (!Array.isArray(foodIds)) {
foodIds = [foodIds];
quantities = [quantities];
}
const meal = await this.mealModel.create({
UserId: req.user.id,
date: date || new Date(),
meal_type,
time: time || null,
});
for (let i = 0; i < foodIds.length; i++) {
const foodId = foodIds[i];
const quantity = quantities[i];
if (foodId && quantity) {
const food = await this.foodItemModel.findByPk(foodId);
if (food) {
await this.mealFoodModel.create({
MealId: meal.id,
FoodItemId: foodId,
quantity: quantity,
calories_consumed: food.calories * quantity,
protein_consumed: food.protein_g * quantity,
carbs_consumed: food.carbs_g * quantity,
fat_consumed: food.fat_g * quantity,
});
}
}
}
await this.utilsService.updateDailySummary(req.user.id, date);
req.flash('success_msg', 'Meal added successfully!');
res.redirect('/dashboard');
}
catch (err) {
console.error(err);
req.flash('error_msg', 'Error adding meal');
res.redirect('/add-meal');
}
}
async searchFood(query) {
if (!query || query.length < 2) {
return [];
}
return this.nutritionService.searchAllSources(query);
}
async addFood(body) {
try {
const food = await this.nutritionService.saveFoodToDb(body);
if (food) {
return {
success: true,
food_id: food.id,
name: food.name,
};
}
else {
return { success: false };
}
}
catch (err) {
console.error(err);
return { success: false };
}
}
async addWater(req, res, body) {
try {
const amountMl = parseInt(body.amount_ml) || 250;
const date = body.date || new Date().toISOString().split('T')[0];
await this.waterLogModel.create({
UserId: req.user.id,
date: date,
amount_ml: amountMl,
time: new Date().toISOString(),
});
await this.utilsService.updateDailySummary(req.user.id, date);
req.flash('success_msg', `Added ${amountMl}ml of water!`);
res.redirect('/dashboard');
}
catch (err) {
console.error(err);
req.flash('error_msg', 'Error adding water');
res.redirect('/dashboard');
}
}
async addWeight(req, res, body) {
try {
const weightKg = parseFloat(body.weight_kg);
const date = body.date || new Date().toISOString().split('T')[0];
const weightLog = await this.weightLogModel.findOne({
where: { UserId: req.user.id, date: date },
});
if (weightLog) {
weightLog.weight_kg = weightKg;
weightLog.time = new Date().toISOString();
await weightLog.save();
}
else {
await this.weightLogModel.create({
UserId: req.user.id,
date: date,
weight_kg: weightKg,
time: new Date().toISOString(),
});
}
await this.utilsService.updateDailySummary(req.user.id, date);
res.redirect('/dashboard');
}
catch (err) {
console.error(err);
req.flash('error_msg', 'Error adding weight');
res.redirect('/dashboard');
}
}
};
exports.MealsController = MealsController;
__decorate([
(0, common_1.Get)('add-meal'),
__param(0, (0, common_1.Req)()),
__param(1, (0, common_1.Res)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", void 0)
], MealsController.prototype, "addMealPage", null);
__decorate([
(0, common_1.Post)('add-meal'),
__param(0, (0, common_1.Req)()),
__param(1, (0, common_1.Res)()),
__param(2, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object, Object]),
__metadata("design:returntype", Promise)
], MealsController.prototype, "addMeal", null);
__decorate([
(0, common_1.Get)('api/search-food'),
__param(0, (0, common_1.Query)('q')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], MealsController.prototype, "searchFood", null);
__decorate([
(0, common_1.Post)('api/add-food'),
__param(0, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], MealsController.prototype, "addFood", null);
__decorate([
(0, common_1.Post)('add-water'),
__param(0, (0, common_1.Req)()),
__param(1, (0, common_1.Res)()),
__param(2, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object, Object]),
__metadata("design:returntype", Promise)
], MealsController.prototype, "addWater", null);
__decorate([
(0, common_1.Post)('add-weight'),
__param(0, (0, common_1.Req)()),
__param(1, (0, common_1.Res)()),
__param(2, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object, Object]),
__metadata("design:returntype", Promise)
], MealsController.prototype, "addWeight", null);
exports.MealsController = MealsController = __decorate([
(0, common_1.Controller)(),
(0, common_1.UseGuards)(authenticated_guard_1.AuthenticatedGuard),
__param(2, (0, sequelize_1.InjectModel)(meal_model_1.Meal)),
__param(3, (0, sequelize_1.InjectModel)(meal_food_model_1.MealFood)),
__param(4, (0, sequelize_1.InjectModel)(food_item_model_1.FoodItem)),
__param(5, (0, sequelize_1.InjectModel)(water_log_model_1.WaterLog)),
__param(6, (0, sequelize_1.InjectModel)(weight_log_model_1.WeightLog)),
__metadata("design:paramtypes", [utils_service_1.UtilsService,
nutrition_service_1.NutritionService, Object, Object, Object, Object, Object])
], MealsController);
//# sourceMappingURL=meals.controller.js.map

1
dist/meals/meals.controller.js.map vendored Normal file

File diff suppressed because one or more lines are too long

2
dist/meals/meals.module.d.ts vendored Normal file
View File

@@ -0,0 +1,2 @@
export declare class MealsModule {
}

39
dist/meals/meals.module.js vendored Normal file
View File

@@ -0,0 +1,39 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MealsModule = void 0;
const common_1 = require("@nestjs/common");
const sequelize_1 = require("@nestjs/sequelize");
const meals_controller_1 = require("./meals.controller");
const nutrition_service_1 = require("./nutrition.service");
const meal_model_1 = require("../models/meal.model");
const meal_food_model_1 = require("../models/meal-food.model");
const food_item_model_1 = require("../models/food-item.model");
const water_log_model_1 = require("../models/water-log.model");
const weight_log_model_1 = require("../models/weight-log.model");
const api_cache_model_1 = require("../models/api-cache.model");
let MealsModule = class MealsModule {
};
exports.MealsModule = MealsModule;
exports.MealsModule = MealsModule = __decorate([
(0, common_1.Module)({
imports: [
sequelize_1.SequelizeModule.forFeature([
meal_model_1.Meal,
meal_food_model_1.MealFood,
food_item_model_1.FoodItem,
water_log_model_1.WaterLog,
weight_log_model_1.WeightLog,
api_cache_model_1.APICache,
]),
],
controllers: [meals_controller_1.MealsController],
providers: [nutrition_service_1.NutritionService],
})
], MealsModule);
//# sourceMappingURL=meals.module.js.map

1
dist/meals/meals.module.js.map vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"meals.module.js","sourceRoot":"","sources":["../../src/meals/meals.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,iDAAoD;AACpD,yDAAqD;AACrD,2DAAuD;AACvD,qDAA4C;AAC5C,+DAAqD;AACrD,+DAAqD;AACrD,+DAAqD;AACrD,iEAAuD;AACvD,+DAAqD;AAgB9C,IAAM,WAAW,GAAjB,MAAM,WAAW;CAAG,CAAA;AAAd,kCAAW;sBAAX,WAAW;IAdvB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,2BAAe,CAAC,UAAU,CAAC;gBACzB,iBAAI;gBACJ,0BAAQ;gBACR,0BAAQ;gBACR,0BAAQ;gBACR,4BAAS;gBACT,0BAAQ;aACT,CAAC;SACH;QACD,WAAW,EAAE,CAAC,kCAAe,CAAC;QAC9B,SAAS,EAAE,CAAC,oCAAgB,CAAC;KAC9B,CAAC;GACW,WAAW,CAAG"}

52
dist/meals/nutrition.service.d.ts vendored Normal file
View File

@@ -0,0 +1,52 @@
import { ConfigService } from '@nestjs/config';
import { FoodItem } from '../models/food-item.model';
import { APICache } from '../models/api-cache.model';
export declare class NutritionService {
private configService;
private foodItemModel;
private apiCacheModel;
private apiKey;
private baseUrl;
private headers;
private cacheDurationDays;
constructor(configService: ConfigService, foodItemModel: typeof FoodItem, apiCacheModel: typeof APICache);
searchFood(query: string): Promise<{
name: any;
calories: any;
protein_g: any;
carbs_g: any;
fat_g: any;
fiber_g: any;
sugar_g: any;
sodium_mg: any;
serving_size_g: any;
source: string;
}[]>;
_getFromCache(query: string): Promise<{
name: any;
calories: any;
protein_g: any;
carbs_g: any;
fat_g: any;
fiber_g: any;
sugar_g: any;
sodium_mg: any;
serving_size_g: any;
source: string;
}[]>;
_saveToCache(query: string, source: string, data: any): Promise<void>;
_parseApiResponse(data: any[]): {
name: any;
calories: any;
protein_g: any;
carbs_g: any;
fat_g: any;
fiber_g: any;
sugar_g: any;
sodium_mg: any;
serving_size_g: any;
source: string;
}[];
saveFoodToDb(foodData: any): Promise<FoodItem>;
searchAllSources(query: string): Promise<any[]>;
}

215
dist/meals/nutrition.service.js vendored Normal file
View File

@@ -0,0 +1,215 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.NutritionService = void 0;
const common_1 = require("@nestjs/common");
const sequelize_1 = require("@nestjs/sequelize");
const config_1 = require("@nestjs/config");
const sequelize_2 = require("sequelize");
const axios_1 = require("axios");
const food_item_model_1 = require("../models/food-item.model");
const api_cache_model_1 = require("../models/api-cache.model");
let NutritionService = class NutritionService {
constructor(configService, foodItemModel, apiCacheModel) {
this.configService = configService;
this.foodItemModel = foodItemModel;
this.apiCacheModel = apiCacheModel;
this.apiKey = this.configService.get('API_NINJAS_KEY');
this.baseUrl = 'https://api.api-ninjas.com/v1/nutrition';
this.headers = { 'X-Api-Key': this.apiKey };
this.cacheDurationDays = 30;
}
async searchFood(query) {
const cached = await this._getFromCache(query);
if (cached) {
return cached;
}
try {
if (!this.apiKey) {
console.warn('API_NINJAS_KEY is not set');
return [];
}
const response = await axios_1.default.get(this.baseUrl, {
headers: this.headers,
params: { query: query },
timeout: 10000,
});
if (response.status === 200) {
const data = response.data;
await this._saveToCache(query, 'api_ninjas', data);
return this._parseApiResponse(data);
}
else {
console.error(`API Error: ${response.status}`);
return [];
}
}
catch (error) {
console.error(`API Request failed: ${error.message}`);
return [];
}
}
async _getFromCache(query) {
const cacheEntry = await this.apiCacheModel.findOne({
where: { query: query.toLowerCase() },
});
if (cacheEntry) {
const age = (new Date().getTime() - new Date(cacheEntry.cached_at).getTime()) / (1000 * 60 * 60 * 24);
if (age < this.cacheDurationDays) {
const data = JSON.parse(cacheEntry.response_json);
return this._parseApiResponse(data);
}
}
return null;
}
async _saveToCache(query, source, data) {
try {
let cacheEntry = await this.apiCacheModel.findOne({
where: { query: query.toLowerCase() },
});
if (cacheEntry) {
cacheEntry.response_json = JSON.stringify(data);
cacheEntry.cached_at = new Date();
await cacheEntry.save();
}
else {
await this.apiCacheModel.create({
query: query.toLowerCase(),
api_source: source,
response_json: JSON.stringify(data),
cached_at: new Date(),
});
}
}
catch (error) {
console.error(`Cache save failed: ${error.message}`);
}
}
_parseApiResponse(data) {
return data.map((item) => ({
name: (item.name || '').replace(/\b\w/g, (l) => l.toUpperCase()),
calories: item.calories || 0,
protein_g: item.protein_g || 0,
carbs_g: item.carbohydrates_total_g || 0,
fat_g: item.fat_total_g || 0,
fiber_g: item.fiber_g || 0,
sugar_g: item.sugar_g || 0,
sodium_mg: item.sodium_mg || 0,
serving_size_g: item.serving_size_g || 100,
source: 'api_ninjas',
}));
}
async saveFoodToDb(foodData) {
try {
const existing = await this.foodItemModel.findOne({
where: {
name: foodData.name,
source: foodData.source || 'api',
},
});
if (existing) {
return existing;
}
const food = await this.foodItemModel.create({
name: foodData.name,
calories: foodData.calories,
protein_g: foodData.protein_g || 0,
carbs_g: foodData.carbs_g || 0,
fat_g: foodData.fat_g || 0,
fiber_g: foodData.fiber_g || 0,
sugar_g: foodData.sugar_g || 0,
sodium_mg: foodData.sodium_mg || 0,
serving_size_g: foodData.serving_size_g || 100,
serving_description: foodData.serving_description || '1 serving',
source: foodData.source || 'api',
api_data: JSON.stringify(foodData),
});
return food;
}
catch (error) {
console.error(`Error saving food to DB: ${error.message}`);
return null;
}
}
async searchAllSources(query) {
const results = [];
const filipinoFoods = await this.foodItemModel.findAll({
where: {
is_filipino: true,
[sequelize_2.Op.or]: [
{ name: { [sequelize_2.Op.like]: `%${query}%` } },
{ name_tagalog: { [sequelize_2.Op.like]: `%${query}%` } },
],
},
limit: 5,
});
for (const food of filipinoFoods) {
results.push({
id: food.id,
name: food.name,
name_tagalog: food.name_tagalog,
calories: food.calories,
protein_g: food.protein_g,
carbs_g: food.carbs_g,
fat_g: food.fat_g,
serving_description: food.serving_description,
source: 'filipino',
category: food.category,
});
}
const otherFoods = await this.foodItemModel.findAll({
where: {
is_filipino: false,
name: { [sequelize_2.Op.like]: `%${query}%` },
},
limit: 5,
});
for (const food of otherFoods) {
results.push({
id: food.id,
name: food.name,
calories: food.calories,
protein_g: food.protein_g,
carbs_g: food.carbs_g,
fat_g: food.fat_g,
serving_description: food.serving_description,
source: food.source,
});
}
if (results.length < 3 && this.apiKey) {
const apiResults = await this.searchFood(query);
for (const foodData of apiResults.slice(0, 5)) {
results.push({
name: foodData.name,
calories: foodData.calories,
protein_g: foodData.protein_g,
carbs_g: foodData.carbs_g || 0,
fat_g: foodData.fat_g || 0,
serving_description: '100g',
source: 'api_ninjas',
raw_data: foodData,
});
}
}
return results;
}
};
exports.NutritionService = NutritionService;
exports.NutritionService = NutritionService = __decorate([
(0, common_1.Injectable)(),
__param(1, (0, sequelize_1.InjectModel)(food_item_model_1.FoodItem)),
__param(2, (0, sequelize_1.InjectModel)(api_cache_model_1.APICache)),
__metadata("design:paramtypes", [config_1.ConfigService, Object, Object])
], NutritionService);
//# sourceMappingURL=nutrition.service.js.map

1
dist/meals/nutrition.service.js.map vendored Normal file

File diff suppressed because one or more lines are too long