first commit

This commit is contained in:
dwindown
2025-10-09 12:52:41 +07:00
commit 0da6071eb3
205 changed files with 30980 additions and 0 deletions

BIN
apps/api/.DS_Store vendored Normal file

Binary file not shown.

16
apps/api/.env.example Normal file
View File

@@ -0,0 +1,16 @@
# Database Configuration
DATABASE_URL="postgresql://username:password@localhost:5432/tabungin_dev"
SHADOW_DATABASE_URL="postgresql://username:password@localhost:5432/tabungin_shadow"
# Firebase Admin SDK Configuration
# Get these from Firebase Console > Project Settings > Service Accounts
FIREBASE_PROJECT_ID=your_project_id
FIREBASE_CLIENT_EMAIL=firebase-adminsdk-xxxxx@your_project_id.iam.gserviceaccount.com
FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nYOUR_PRIVATE_KEY_HERE\n-----END PRIVATE KEY-----\n"
# API Configuration
PORT=3000
WEB_APP_URL=http://localhost:5173
# Development User ID (run seed script to create this user)
TEMP_USER_ID=16b74848-daa3-4dc9-8de2-3cf59e08f8e3

5
apps/api/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
node_modules
# Keep environment variables out of version control
.env
/generated/prisma

4
apps/api/.prettierrc Normal file
View File

@@ -0,0 +1,4 @@
{
"singleQuote": true,
"trailingComma": "all"
}

98
apps/api/README.md Normal file
View File

@@ -0,0 +1,98 @@
<p align="center">
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
</p>
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
<p align="center">
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg" alt="Donate us"/></a>
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow" alt="Follow us on Twitter"></a>
</p>
<!--[![Backers on Open Collective](https://opencollective.com/nest/backers/badge.svg)](https://opencollective.com/nest#backer)
[![Sponsors on Open Collective](https://opencollective.com/nest/sponsors/badge.svg)](https://opencollective.com/nest#sponsor)-->
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Project setup
```bash
$ npm install
```
## Compile and run the project
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Run tests
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Deployment
When you're ready to deploy your NestJS application to production, there are some key steps you can take to ensure it runs as efficiently as possible. Check out the [deployment documentation](https://docs.nestjs.com/deployment) for more information.
If you are looking for a cloud-based platform to deploy your NestJS application, check out [Mau](https://mau.nestjs.com), our official platform for deploying NestJS applications on AWS. Mau makes deployment straightforward and fast, requiring just a few simple steps:
```bash
$ npm install -g @nestjs/mau
$ mau deploy
```
With Mau, you can deploy your application in just a few clicks, allowing you to focus on building features rather than managing infrastructure.
## Resources
Check out a few resources that may come in handy when working with NestJS:
- Visit the [NestJS Documentation](https://docs.nestjs.com) to learn more about the framework.
- For questions and support, please visit our [Discord channel](https://discord.gg/G7Qnnhy).
- To dive deeper and get more hands-on experience, check out our official video [courses](https://courses.nestjs.com/).
- Deploy your application to AWS with the help of [NestJS Mau](https://mau.nestjs.com) in just a few clicks.
- Visualize your application graph and interact with the NestJS application in real-time using [NestJS Devtools](https://devtools.nestjs.com).
- Need help with your project (part-time to full-time)? Check out our official [enterprise support](https://enterprise.nestjs.com).
- To stay in the loop and get updates, follow us on [X](https://x.com/nestframework) and [LinkedIn](https://linkedin.com/company/nestjs).
- Looking for a job, or have a job to offer? Check out our official [Jobs board](https://jobs.nestjs.com).
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE).

6
apps/api/dist/app.controller.d.ts vendored Normal file
View File

@@ -0,0 +1,6 @@
import { AppService } from './app.service';
export declare class AppController {
private readonly appService;
constructor(appService: AppService);
getHello(): string;
}

35
apps/api/dist/app.controller.js vendored Normal file
View File

@@ -0,0 +1,35 @@
"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);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AppController = void 0;
const common_1 = require("@nestjs/common");
const app_service_1 = require("./app.service");
let AppController = class AppController {
appService;
constructor(appService) {
this.appService = appService;
}
getHello() {
return this.appService.getHello();
}
};
exports.AppController = AppController;
__decorate([
(0, common_1.Get)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", String)
], AppController.prototype, "getHello", null);
exports.AppController = AppController = __decorate([
(0, common_1.Controller)(),
__metadata("design:paramtypes", [app_service_1.AppService])
], AppController);
//# sourceMappingURL=app.controller.js.map

1
apps/api/dist/app.controller.js.map vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"app.controller.js","sourceRoot":"","sources":["../src/app.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAiD;AACjD,+CAA2C;AAGpC,IAAM,aAAa,GAAnB,MAAM,aAAa;IACK;IAA7B,YAA6B,UAAsB;QAAtB,eAAU,GAAV,UAAU,CAAY;IAAG,CAAC;IAGvD,QAAQ;QACN,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;IACpC,CAAC;CACF,CAAA;AAPY,sCAAa;AAIxB;IADC,IAAA,YAAG,GAAE;;;;6CAGL;wBANU,aAAa;IADzB,IAAA,mBAAU,GAAE;qCAE8B,wBAAU;GADxC,aAAa,CAOzB"}

2
apps/api/dist/app.module.d.ts vendored Normal file
View File

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

77
apps/api/dist/app.module.js vendored Normal file
View File

@@ -0,0 +1,77 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
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 __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.AppModule = void 0;
const common_1 = require("@nestjs/common");
const config_1 = require("@nestjs/config");
const path = __importStar(require("path"));
const prisma_module_1 = require("./prisma/prisma.module");
const auth_module_1 = require("./auth/auth.module");
const health_controller_1 = require("./health/health.controller");
const users_module_1 = require("./users/users.module");
const wallets_module_1 = require("./wallets/wallets.module");
const transactions_module_1 = require("./transactions/transactions.module");
const categories_module_1 = require("./categories/categories.module");
let AppModule = class AppModule {
};
exports.AppModule = AppModule;
exports.AppModule = AppModule = __decorate([
(0, common_1.Module)({
imports: [
config_1.ConfigModule.forRoot({
isGlobal: true,
envFilePath: [
path.resolve(process.cwd(), '.env'),
path.resolve(process.cwd(), '../../.env'),
],
}),
prisma_module_1.PrismaModule,
auth_module_1.AuthModule,
users_module_1.UsersModule,
wallets_module_1.WalletsModule,
transactions_module_1.TransactionsModule,
categories_module_1.CategoriesModule,
],
controllers: [health_controller_1.HealthController],
providers: [],
})
], AppModule);
//# sourceMappingURL=app.module.js.map

1
apps/api/dist/app.module.js.map vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"app.module.js","sourceRoot":"","sources":["../src/app.module.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAAwC;AACxC,2CAA8C;AAC9C,2CAA6B;AAC7B,0DAAsD;AACtD,oDAAgD;AAChD,kEAA8D;AAC9D,uDAAmD;AACnD,6DAAyD;AACzD,4EAAwE;AACxE,sEAAkE;AAqB3D,IAAM,SAAS,GAAf,MAAM,SAAS;CAAG,CAAA;AAAZ,8BAAS;oBAAT,SAAS;IAnBrB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,qBAAY,CAAC,OAAO,CAAC;gBACnB,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE;oBACX,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC;oBACnC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,YAAY,CAAC;iBAC1C;aACF,CAAC;YACF,4BAAY;YACZ,wBAAU;YACV,0BAAW;YACX,8BAAa;YACb,wCAAkB;YAClB,oCAAgB;SACjB;QACD,WAAW,EAAE,CAAC,oCAAgB,CAAC;QAC/B,SAAS,EAAE,EAAE;KACd,CAAC;GACW,SAAS,CAAG"}

3
apps/api/dist/app.service.d.ts vendored Normal file
View File

@@ -0,0 +1,3 @@
export declare class AppService {
getHello(): string;
}

20
apps/api/dist/app.service.js vendored Normal file
View File

@@ -0,0 +1,20 @@
"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.AppService = void 0;
const common_1 = require("@nestjs/common");
let AppService = class AppService {
getHello() {
return 'Hello World!';
}
};
exports.AppService = AppService;
exports.AppService = AppService = __decorate([
(0, common_1.Injectable)()
], AppService);
//# sourceMappingURL=app.service.js.map

1
apps/api/dist/app.service.js.map vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"app.service.js","sourceRoot":"","sources":["../src/app.service.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4C;AAGrC,IAAM,UAAU,GAAhB,MAAM,UAAU;IACrB,QAAQ;QACN,OAAO,cAAc,CAAC;IACxB,CAAC;CACF,CAAA;AAJY,gCAAU;qBAAV,UAAU;IADtB,IAAA,mBAAU,GAAE;GACA,UAAU,CAItB"}

8
apps/api/dist/auth/auth.guard.d.ts vendored Normal file
View File

@@ -0,0 +1,8 @@
import { CanActivate, ExecutionContext } from '@nestjs/common';
import { FirebaseService } from './firebase.service';
export declare class AuthGuard implements CanActivate {
private firebaseService;
constructor(firebaseService: FirebaseService);
canActivate(context: ExecutionContext): Promise<boolean>;
private extractTokenFromHeader;
}

49
apps/api/dist/auth/auth.guard.js vendored Normal file
View File

@@ -0,0 +1,49 @@
"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);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthGuard = void 0;
const common_1 = require("@nestjs/common");
const firebase_service_1 = require("./firebase.service");
let AuthGuard = class AuthGuard {
firebaseService;
constructor(firebaseService) {
this.firebaseService = firebaseService;
}
async canActivate(context) {
const request = context.switchToHttp().getRequest();
if (!this.firebaseService.isFirebaseConfigured()) {
console.warn('⚠️ Firebase not configured - allowing request without auth');
return true;
}
const token = this.extractTokenFromHeader(request);
if (!token) {
throw new common_1.UnauthorizedException('No token provided');
}
try {
const decodedToken = await this.firebaseService.verifyIdToken(token);
request.user = decodedToken;
return true;
}
catch (error) {
throw new common_1.UnauthorizedException('Invalid token');
}
}
extractTokenFromHeader(request) {
const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
}
};
exports.AuthGuard = AuthGuard;
exports.AuthGuard = AuthGuard = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [firebase_service_1.FirebaseService])
], AuthGuard);
//# sourceMappingURL=auth.guard.js.map

1
apps/api/dist/auth/auth.guard.js.map vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"auth.guard.js","sourceRoot":"","sources":["../../src/auth/auth.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAkG;AAClG,yDAAqD;AAG9C,IAAM,SAAS,GAAf,MAAM,SAAS;IACA;IAApB,YAAoB,eAAgC;QAAhC,oBAAe,GAAf,eAAe,CAAiB;IAAG,CAAC;IAExD,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;QAGpD,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,oBAAoB,EAAE,EAAE,CAAC;YACjD,OAAO,CAAC,IAAI,CAAC,4DAA4D,CAAC,CAAC;YAC3E,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;QAEnD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,8BAAqB,CAAC,mBAAmB,CAAC,CAAC;QACvD,CAAC;QAED,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YACrE,OAAO,CAAC,IAAI,GAAG,YAAY,CAAC;YAC5B,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,8BAAqB,CAAC,eAAe,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAEO,sBAAsB,CAAC,OAAY;QACzC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,EAAE,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACtE,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/C,CAAC;CACF,CAAA;AA/BY,8BAAS;oBAAT,SAAS;IADrB,IAAA,mBAAU,GAAE;qCAE0B,kCAAe;GADzC,SAAS,CA+BrB"}

2
apps/api/dist/auth/auth.module.d.ts vendored Normal file
View File

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

22
apps/api/dist/auth/auth.module.js vendored Normal file
View File

@@ -0,0 +1,22 @@
"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.AuthModule = void 0;
const common_1 = require("@nestjs/common");
const firebase_service_1 = require("./firebase.service");
const auth_guard_1 = require("./auth.guard");
let AuthModule = class AuthModule {
};
exports.AuthModule = AuthModule;
exports.AuthModule = AuthModule = __decorate([
(0, common_1.Module)({
providers: [firebase_service_1.FirebaseService, auth_guard_1.AuthGuard],
exports: [firebase_service_1.FirebaseService, auth_guard_1.AuthGuard],
})
], AuthModule);
//# sourceMappingURL=auth.module.js.map

1
apps/api/dist/auth/auth.module.js.map vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"auth.module.js","sourceRoot":"","sources":["../../src/auth/auth.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,yDAAqD;AACrD,6CAAyC;AAMlC,IAAM,UAAU,GAAhB,MAAM,UAAU;CAAG,CAAA;AAAb,gCAAU;qBAAV,UAAU;IAJtB,IAAA,eAAM,EAAC;QACN,SAAS,EAAE,CAAC,kCAAe,EAAE,sBAAS,CAAC;QACvC,OAAO,EAAE,CAAC,kCAAe,EAAE,sBAAS,CAAC;KACtC,CAAC;GACW,UAAU,CAAG"}

View File

@@ -0,0 +1,9 @@
import * as admin from 'firebase-admin';
export declare class FirebaseService {
private app;
private isConfigured;
constructor();
verifyIdToken(idToken: string): Promise<admin.auth.DecodedIdToken>;
getUser(uid: string): Promise<admin.auth.UserRecord>;
isFirebaseConfigured(): boolean;
}

113
apps/api/dist/auth/firebase.service.js vendored Normal file
View File

@@ -0,0 +1,113 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
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 __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FirebaseService = void 0;
const common_1 = require("@nestjs/common");
const admin = __importStar(require("firebase-admin"));
let FirebaseService = class FirebaseService {
app = null;
isConfigured = false;
constructor() {
const projectId = process.env.FIREBASE_PROJECT_ID;
const clientEmail = process.env.FIREBASE_CLIENT_EMAIL;
const privateKey = process.env.FIREBASE_PRIVATE_KEY;
if (projectId && clientEmail && privateKey) {
try {
if (!admin.apps.length) {
this.app = admin.initializeApp({
credential: admin.credential.cert({
projectId,
clientEmail,
privateKey: privateKey.replace(/\\n/g, '\n'),
}),
});
}
else {
this.app = admin.app();
}
this.isConfigured = true;
console.log('✅ Firebase Admin initialized successfully');
}
catch (error) {
console.warn('⚠️ Firebase Admin initialization failed:', error.message);
this.isConfigured = false;
}
}
else {
console.warn('⚠️ Firebase credentials not found. Auth will use fallback mode.');
this.isConfigured = false;
}
}
async verifyIdToken(idToken) {
if (!this.isConfigured || !this.app) {
throw new Error('Firebase not configured');
}
try {
return await admin.auth().verifyIdToken(idToken);
}
catch (error) {
throw new Error('Invalid token');
}
}
async getUser(uid) {
if (!this.isConfigured || !this.app) {
throw new Error('Firebase not configured');
}
try {
return await admin.auth().getUser(uid);
}
catch (error) {
throw new Error('User not found');
}
}
isFirebaseConfigured() {
return this.isConfigured;
}
};
exports.FirebaseService = FirebaseService;
exports.FirebaseService = FirebaseService = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [])
], FirebaseService);
//# sourceMappingURL=firebase.service.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"firebase.service.js","sourceRoot":"","sources":["../../src/auth/firebase.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAA4C;AAC5C,sDAAwC;AAGjC,IAAM,eAAe,GAArB,MAAM,eAAe;IAClB,GAAG,GAAyB,IAAI,CAAC;IACjC,YAAY,GAAY,KAAK,CAAC;IAEtC;QAEE,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;QAClD,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;QACtD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;QAEpD,IAAI,SAAS,IAAI,WAAW,IAAI,UAAU,EAAE,CAAC;YAC3C,IAAI,CAAC;gBACH,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;oBACvB,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC,aAAa,CAAC;wBAC7B,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;4BAChC,SAAS;4BACT,WAAW;4BACX,UAAU,EAAE,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC;yBAC7C,CAAC;qBACH,CAAC,CAAC;gBACL,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;gBACzB,CAAC;gBACD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;gBACzB,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;YAC3D,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,IAAI,CAAC,0CAA0C,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;gBACxE,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;YAC5B,CAAC;QACH,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,iEAAiE,CAAC,CAAC;YAChF,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,OAAe;QACjC,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;QAC7C,CAAC;QACD,IAAI,CAAC;YACH,OAAO,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QACnD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAW;QACvB,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;QAC7C,CAAC;QACD,IAAI,CAAC;YACH,OAAO,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACzC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAED,oBAAoB;QAClB,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;CACF,CAAA;AA5DY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,mBAAU,GAAE;;GACA,eAAe,CA4D3B"}

View File

@@ -0,0 +1,28 @@
import { CategoriesService } from '../categories/categories.service';
import { CreateCategoryDto } from '../categories/dto/create-category.dto';
export declare class CategoriesController {
private readonly categoriesService;
constructor(categoriesService: CategoriesService);
private userId;
create(createCategoryDto: CreateCategoryDto): Promise<{
id: string;
createdAt: Date;
updatedAt: Date;
name: string;
userId: string;
}>;
findAll(): Promise<{
id: string;
createdAt: Date;
updatedAt: Date;
name: string;
userId: string;
}[]>;
remove(id: string): Promise<{
id: string;
createdAt: Date;
updatedAt: Date;
name: string;
userId: string;
}>;
}

View File

@@ -0,0 +1,66 @@
"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.CategoriesController = void 0;
const common_1 = require("@nestjs/common");
const categories_service_1 = require("../categories/categories.service");
const create_category_dto_1 = require("../categories/dto/create-category.dto");
const user_util_1 = require("../common/user.util");
let CategoriesController = class CategoriesController {
categoriesService;
constructor(categoriesService) {
this.categoriesService = categoriesService;
}
userId() {
return (0, user_util_1.getTempUserId)();
}
create(createCategoryDto) {
return this.categoriesService.create({
...createCategoryDto,
userId: this.userId(),
});
}
findAll() {
return this.categoriesService.findAll(this.userId());
}
remove(id) {
return this.categoriesService.remove(id, this.userId());
}
};
exports.CategoriesController = CategoriesController;
__decorate([
(0, common_1.Post)(),
__param(0, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [create_category_dto_1.CreateCategoryDto]),
__metadata("design:returntype", void 0)
], CategoriesController.prototype, "create", null);
__decorate([
(0, common_1.Get)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", void 0)
], CategoriesController.prototype, "findAll", null);
__decorate([
(0, common_1.Delete)(':id'),
__param(0, (0, common_1.Param)('id')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", void 0)
], CategoriesController.prototype, "remove", null);
exports.CategoriesController = CategoriesController = __decorate([
(0, common_1.Controller)('categories'),
__metadata("design:paramtypes", [categories_service_1.CategoriesService])
], CategoriesController);
//# sourceMappingURL=categories.controller.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"categories.controller.js","sourceRoot":"","sources":["../../src/categories/categories.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAOwB;AACxB,yEAAqE;AACrE,+EAA0E;AAC1E,mDAAoD;AAG7C,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IACF;IAA7B,YAA6B,iBAAoC;QAApC,sBAAiB,GAAjB,iBAAiB,CAAmB;IAAG,CAAC;IAE7D,MAAM;QACZ,OAAO,IAAA,yBAAa,GAAE,CAAC;IACzB,CAAC;IAGD,MAAM,CAAS,iBAAoC;QACjD,OAAO,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC;YACnC,GAAG,iBAAiB;YACpB,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;SACtB,CAAC,CAAC;IACL,CAAC;IAGD,OAAO;QACL,OAAO,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IACvD,CAAC;IAGD,MAAM,CAAc,EAAU;QAC5B,OAAO,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1D,CAAC;CACF,CAAA;AAxBY,oDAAoB;AAQ/B;IADC,IAAA,aAAI,GAAE;IACC,WAAA,IAAA,aAAI,GAAE,CAAA;;qCAAoB,uCAAiB;;kDAKlD;AAGD;IADC,IAAA,YAAG,GAAE;;;;mDAGL;AAGD;IADC,IAAA,eAAM,EAAC,KAAK,CAAC;IACN,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;kDAElB;+BAvBU,oBAAoB;IADhC,IAAA,mBAAU,EAAC,YAAY,CAAC;qCAEyB,sCAAiB;GADtD,oBAAoB,CAwBhC"}

View File

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

View File

@@ -0,0 +1,26 @@
"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.CategoriesModule = void 0;
const common_1 = require("@nestjs/common");
const categories_service_1 = require("./categories.service");
const categories_controller_1 = require("./categories.controller");
const prisma_module_1 = require("../prisma/prisma.module");
const auth_module_1 = require("../auth/auth.module");
let CategoriesModule = class CategoriesModule {
};
exports.CategoriesModule = CategoriesModule;
exports.CategoriesModule = CategoriesModule = __decorate([
(0, common_1.Module)({
imports: [prisma_module_1.PrismaModule, auth_module_1.AuthModule],
controllers: [categories_controller_1.CategoriesController],
providers: [categories_service_1.CategoriesService],
exports: [categories_service_1.CategoriesService],
})
], CategoriesModule);
//# sourceMappingURL=categories.module.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"categories.module.js","sourceRoot":"","sources":["../../src/categories/categories.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,6DAAyD;AACzD,mEAA+D;AAC/D,2DAAuD;AACvD,qDAAiD;AAQ1C,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;CAAG,CAAA;AAAnB,4CAAgB;2BAAhB,gBAAgB;IAN5B,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,4BAAY,EAAE,wBAAU,CAAC;QACnC,WAAW,EAAE,CAAC,4CAAoB,CAAC;QACnC,SAAS,EAAE,CAAC,sCAAiB,CAAC;QAC9B,OAAO,EAAE,CAAC,sCAAiB,CAAC;KAC7B,CAAC;GACW,gBAAgB,CAAG"}

View File

@@ -0,0 +1,30 @@
import { PrismaService } from '../prisma/prisma.service';
import { CreateCategoryDto } from './dto/create-category.dto';
export declare class CategoriesService {
private prisma;
constructor(prisma: PrismaService);
create(data: CreateCategoryDto & {
userId: string;
}): Promise<{
id: string;
createdAt: Date;
updatedAt: Date;
name: string;
userId: string;
}>;
findAll(userId: string): Promise<{
id: string;
createdAt: Date;
updatedAt: Date;
name: string;
userId: string;
}[]>;
remove(id: string, userId: string): Promise<{
id: string;
createdAt: Date;
updatedAt: Date;
name: string;
userId: string;
}>;
findOrCreate(names: string[], userId: string): Promise<any[]>;
}

View File

@@ -0,0 +1,74 @@
"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);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CategoriesService = void 0;
const common_1 = require("@nestjs/common");
const prisma_service_1 = require("../prisma/prisma.service");
let CategoriesService = class CategoriesService {
prisma;
constructor(prisma) {
this.prisma = prisma;
}
async create(data) {
try {
return await this.prisma.category.create({
data: {
name: data.name,
userId: data.userId,
},
});
}
catch (error) {
if (error.code === 'P2002') {
throw new common_1.ConflictException('Category already exists');
}
throw error;
}
}
async findAll(userId) {
return this.prisma.category.findMany({
where: { userId },
orderBy: { name: 'asc' },
});
}
async remove(id, userId) {
const category = await this.prisma.category.findFirst({
where: { id, userId },
});
if (!category) {
throw new common_1.NotFoundException('Category not found');
}
return this.prisma.category.delete({
where: { id },
});
}
async findOrCreate(names, userId) {
const categories = [];
for (const name of names) {
let category = await this.prisma.category.findFirst({
where: { name, userId },
});
if (!category) {
category = await this.prisma.category.create({
data: { name, userId },
});
}
categories.push(category);
}
return categories;
}
};
exports.CategoriesService = CategoriesService;
exports.CategoriesService = CategoriesService = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [prisma_service_1.PrismaService])
], CategoriesService);
//# sourceMappingURL=categories.service.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"categories.service.js","sourceRoot":"","sources":["../../src/categories/categories.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAkF;AAClF,6DAAyD;AAIlD,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IACR;IAApB,YAAoB,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAE7C,KAAK,CAAC,MAAM,CAAC,IAA4C;QACvD,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;gBACvC,IAAI,EAAE;oBACJ,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,MAAM,EAAE,IAAI,CAAC,MAAM;iBACpB;aACF,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBAC3B,MAAM,IAAI,0BAAiB,CAAC,yBAAyB,CAAC,CAAC;YACzD,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,MAAc;QAC1B,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;YACnC,KAAK,EAAE,EAAE,MAAM,EAAE;YACjB,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;SACzB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU,EAAE,MAAc;QACrC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;YACpD,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;SACtB,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,0BAAiB,CAAC,oBAAoB,CAAC,CAAC;QACpD,CAAC;QAED,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;YACjC,KAAK,EAAE,EAAE,EAAE,EAAE;SACd,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,KAAe,EAAE,MAAc;QAChD,MAAM,UAAU,GAAU,EAAE,CAAC;QAE7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;gBAClD,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;aACxB,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;oBAC3C,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;iBACvB,CAAC,CAAC;YACL,CAAC;YAED,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC5B,CAAC;QAED,OAAO,UAAU,CAAC;IACpB,CAAC;CACF,CAAA;AA3DY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAEiB,8BAAa;GAD9B,iBAAiB,CA2D7B"}

View File

@@ -0,0 +1,3 @@
export declare class CreateCategoryDto {
name: string;
}

View File

@@ -0,0 +1,24 @@
"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);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CreateCategoryDto = void 0;
const class_validator_1 = require("class-validator");
class CreateCategoryDto {
name;
}
exports.CreateCategoryDto = CreateCategoryDto;
__decorate([
(0, class_validator_1.IsString)(),
(0, class_validator_1.IsNotEmpty)(),
(0, class_validator_1.MaxLength)(50),
__metadata("design:type", String)
], CreateCategoryDto.prototype, "name", void 0);
//# sourceMappingURL=create-category.dto.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"create-category.dto.js","sourceRoot":"","sources":["../../../src/categories/dto/create-category.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDAAkE;AAElE,MAAa,iBAAiB;IAI5B,IAAI,CAAS;CACd;AALD,8CAKC;AADC;IAHC,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,GAAE;IACZ,IAAA,2BAAS,EAAC,EAAE,CAAC;;+CACD"}

3
apps/api/dist/common/user.util.d.ts vendored Normal file
View File

@@ -0,0 +1,3 @@
export declare function getTempUserId(): string;
export declare function getUserIdFromRequest(request: any): string;
export declare function createUserDecorator(): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;

23
apps/api/dist/common/user.util.js vendored Normal file
View File

@@ -0,0 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getTempUserId = getTempUserId;
exports.getUserIdFromRequest = getUserIdFromRequest;
exports.createUserDecorator = createUserDecorator;
function getTempUserId() {
const id = process.env.TEMP_USER_ID?.trim();
if (!id) {
throw new Error('TEMP_USER_ID is not set. Run the seed and set it in apps/api/.env');
}
return id;
}
function getUserIdFromRequest(request) {
if (request.user?.uid) {
return request.user.uid;
}
return getTempUserId();
}
function createUserDecorator() {
return (target, propertyKey, descriptor) => {
};
}
//# sourceMappingURL=user.util.js.map

1
apps/api/dist/common/user.util.js.map vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"user.util.js","sourceRoot":"","sources":["../../src/common/user.util.ts"],"names":[],"mappings":";;AAAA,sCAMG;AAEH,oDAQC;AAED,kDAKC;AAvBD,SAAgB,aAAa;IACzB,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC;IAC5C,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,MAAM,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAC;IACvF,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAEH,SAAgB,oBAAoB,CAAC,OAAY;IAE/C,IAAI,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC;QACtB,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;IAC1B,CAAC;IAGD,OAAO,aAAa,EAAE,CAAC;AACzB,CAAC;AAED,SAAgB,mBAAmB;IACjC,OAAO,CAAC,MAAW,EAAE,WAAmB,EAAE,UAA8B,EAAE,EAAE;IAG5E,CAAC,CAAC;AACJ,CAAC"}

View File

@@ -0,0 +1,11 @@
import { PrismaService } from '../prisma/prisma.service';
export declare class HealthController {
private readonly prisma;
constructor(prisma: PrismaService);
ok(): {
status: string;
};
db(): Promise<{
db: string;
}>;
}

View File

@@ -0,0 +1,45 @@
"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);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.HealthController = void 0;
const common_1 = require("@nestjs/common");
const prisma_service_1 = require("../prisma/prisma.service");
let HealthController = class HealthController {
prisma;
constructor(prisma) {
this.prisma = prisma;
}
ok() {
return { status: 'ok' };
}
async db() {
await this.prisma.$queryRaw `SELECT 1`;
return { db: 'connected' };
}
};
exports.HealthController = HealthController;
__decorate([
(0, common_1.Get)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", void 0)
], HealthController.prototype, "ok", null);
__decorate([
(0, common_1.Get)('db'),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], HealthController.prototype, "db", null);
exports.HealthController = HealthController = __decorate([
(0, common_1.Controller)('health'),
__metadata("design:paramtypes", [prisma_service_1.PrismaService])
], HealthController);
//# sourceMappingURL=health.controller.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"health.controller.js","sourceRoot":"","sources":["../../src/health/health.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAiD;AACjD,6DAAyD;AAGlD,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IACE;IAA7B,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAGtD,EAAE;QACA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC1B,CAAC;IAGK,AAAN,KAAK,CAAC,EAAE;QAEN,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAA,UAAU,CAAC;QACtC,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,CAAC;IAC7B,CAAC;CACF,CAAA;AAdY,4CAAgB;AAI3B;IADC,IAAA,YAAG,GAAE;;;;0CAGL;AAGK;IADL,IAAA,YAAG,EAAC,IAAI,CAAC;;;;0CAKT;2BAbU,gBAAgB;IAD5B,IAAA,mBAAU,EAAC,QAAQ,CAAC;qCAEkB,8BAAa;GADvC,gBAAgB,CAc5B"}

1
apps/api/dist/main.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
export {};

18
apps/api/dist/main.js vendored Normal file
View File

@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@nestjs/core");
const app_module_1 = require("./app.module");
async function bootstrap() {
const app = await core_1.NestFactory.create(app_module_1.AppModule);
const webOrigin = process.env.WEB_APP_URL ?? 'http://localhost:5173';
app.enableCors({
origin: webOrigin,
credentials: true,
});
app.setGlobalPrefix('api');
const port = process.env.PORT ? Number(process.env.PORT) : 3000;
await app.listen(port);
console.log(`API listening on http://localhost:${port}`);
}
bootstrap();
//# sourceMappingURL=main.js.map

1
apps/api/dist/main.js.map vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;AAAA,uCAA2C;AAC3C,6CAAyC;AAEzC,KAAK,UAAU,SAAS;IACtB,MAAM,GAAG,GAAG,MAAM,kBAAW,CAAC,MAAM,CAAC,sBAAS,CAAC,CAAC;IAGhD,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,uBAAuB,CAAC;IACrE,GAAG,CAAC,UAAU,CAAC;QACb,MAAM,EAAE,SAAS;QACjB,WAAW,EAAE,IAAI;KAClB,CAAC,CAAC;IAGH,GAAG,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAE3B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChE,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAEvB,OAAO,CAAC,GAAG,CAAC,qCAAqC,IAAI,EAAE,CAAC,CAAC;AAC3D,CAAC;AACD,SAAS,EAAE,CAAC"}

View File

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

22
apps/api/dist/prisma/prisma.module.js vendored Normal file
View File

@@ -0,0 +1,22 @@
"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.PrismaModule = void 0;
const common_1 = require("@nestjs/common");
const prisma_service_1 = require("./prisma.service");
let PrismaModule = class PrismaModule {
};
exports.PrismaModule = PrismaModule;
exports.PrismaModule = PrismaModule = __decorate([
(0, common_1.Global)(),
(0, common_1.Module)({
providers: [prisma_service_1.PrismaService],
exports: [prisma_service_1.PrismaService],
})
], PrismaModule);
//# sourceMappingURL=prisma.module.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"prisma.module.js","sourceRoot":"","sources":["../../src/prisma/prisma.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAgD;AAChD,qDAAiD;AAO1C,IAAM,YAAY,GAAlB,MAAM,YAAY;CAAG,CAAA;AAAf,oCAAY;uBAAZ,YAAY;IALxB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACN,SAAS,EAAE,CAAC,8BAAa,CAAC;QAC1B,OAAO,EAAE,CAAC,8BAAa,CAAC;KACzB,CAAC;GACW,YAAY,CAAG"}

View File

@@ -0,0 +1,6 @@
import { INestApplication, OnModuleInit } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
export declare class PrismaService extends PrismaClient implements OnModuleInit {
onModuleInit(): Promise<void>;
enableShutdownHooks(app: INestApplication): Promise<void>;
}

26
apps/api/dist/prisma/prisma.service.js vendored Normal file
View File

@@ -0,0 +1,26 @@
"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.PrismaService = void 0;
const common_1 = require("@nestjs/common");
const client_1 = require("@prisma/client");
let PrismaService = class PrismaService extends client_1.PrismaClient {
async onModuleInit() {
await this.$connect();
}
async enableShutdownHooks(app) {
process.on('beforeExit', async () => {
await app.close();
});
}
};
exports.PrismaService = PrismaService;
exports.PrismaService = PrismaService = __decorate([
(0, common_1.Injectable)()
], PrismaService);
//# sourceMappingURL=prisma.service.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"prisma.service.js","sourceRoot":"","sources":["../../src/prisma/prisma.service.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4E;AAC5E,2CAA8C;AAGvC,IAAM,aAAa,GAAnB,MAAM,aAAc,SAAQ,qBAAY;IAC7C,KAAK,CAAC,YAAY;QAChB,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;IACxB,CAAC;IAGD,KAAK,CAAC,mBAAmB,CAAC,GAAqB;QAC7C,OAAO,CAAC,EAAE,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;YAClC,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAA;AAXY,sCAAa;wBAAb,aAAa;IADzB,IAAA,mBAAU,GAAE;GACA,aAAa,CAWzB"}

1
apps/api/dist/seed.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
export {};

37
apps/api/dist/seed.js vendored Normal file
View File

@@ -0,0 +1,37 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const client_1 = require("@prisma/client");
const prisma = new client_1.PrismaClient();
async function main() {
const userId = '16b74848-daa3-4dc9-8de2-3cf59e08f8e3';
const user = await prisma.user.upsert({
where: { id: userId },
update: {},
create: {
id: userId,
},
});
const existing = await prisma.wallet.findFirst({
where: { userId: user.id, kind: 'money' },
});
if (!existing) {
await prisma.wallet.create({
data: {
userId: user.id,
kind: 'money',
name: 'Cash',
currency: 'IDR',
},
});
}
console.log('Seed complete. TEMP_USER_ID=', user.id);
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
//# sourceMappingURL=seed.js.map

1
apps/api/dist/seed.js.map vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"seed.js","sourceRoot":"","sources":["../src/seed.ts"],"names":[],"mappings":";;AAAA,2CAA8C;AAE9C,MAAM,MAAM,GAAG,IAAI,qBAAY,EAAE,CAAC;AAElC,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,sCAAsC,CAAC;IACtD,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;QACpC,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;QACrB,MAAM,EAAE,EAAE;QACV,MAAM,EAAE;YACN,EAAE,EAAE,MAAM;SACX;KACF,CAAC,CAAC;IAGH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC;QAC7C,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;KAC1C,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;YACzB,IAAI,EAAE;gBACJ,MAAM,EAAE,IAAI,CAAC,EAAE;gBACf,IAAI,EAAE,OAAO;gBACb,IAAI,EAAE,MAAM;gBACZ,QAAQ,EAAE,KAAK;aAChB;SACF,CAAC,CAAC;IACL,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,8BAA8B,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;AACvD,CAAC;AAED,IAAI,EAAE;KACH,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;IACX,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACjB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC;KACD,OAAO,CAAC,KAAK,IAAI,EAAE;IAClB,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC;AAC7B,CAAC,CAAC,CAAC"}

View File

@@ -0,0 +1,12 @@
import { z } from 'zod';
export declare const TransactionUpdateSchema: z.ZodObject<{
amount: z.ZodOptional<z.ZodNumber>;
direction: z.ZodOptional<z.ZodEnum<{
in: "in";
out: "out";
}>>;
date: z.ZodOptional<z.ZodString>;
category: z.ZodOptional<z.ZodNullable<z.ZodString>>;
memo: z.ZodOptional<z.ZodNullable<z.ZodString>>;
}, z.core.$strip>;
export type TransactionUpdateDto = z.infer<typeof TransactionUpdateSchema>;

View File

@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TransactionUpdateSchema = void 0;
const zod_1 = require("zod");
exports.TransactionUpdateSchema = zod_1.z.object({
amount: zod_1.z.number().positive().optional(),
direction: zod_1.z.enum(['in', 'out']).optional(),
date: zod_1.z.string().datetime().optional(),
category: zod_1.z.string().min(1).nullable().optional(),
memo: zod_1.z.string().min(1).nullable().optional(),
});
//# sourceMappingURL=transaction.dto.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"transaction.dto.js","sourceRoot":"","sources":["../../src/transactions/transaction.dto.ts"],"names":[],"mappings":";;;AAAA,6BAAwB;AAEX,QAAA,uBAAuB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC9C,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACxC,SAAS,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC1C,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACtC,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACjD,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CAC9C,CAAC,CAAC"}

View File

@@ -0,0 +1,61 @@
import type { Response } from 'express';
import { TransactionsService } from './transactions.service';
export declare class TransactionsController {
private readonly tx;
constructor(tx: TransactionsService);
list(walletId: string): import("@prisma/client").Prisma.PrismaPromise<{
category: string | null;
id: string;
createdAt: Date;
userId: string;
amount: import("@prisma/client/runtime/library").Decimal;
direction: string;
date: Date;
memo: string | null;
walletId: string;
recurrenceId: string | null;
}[]>;
create(walletId: string, body: {
amount: number | string;
direction: 'in' | 'out';
date?: string;
category?: string;
memo?: string;
}): Promise<{
category: string | null;
id: string;
createdAt: Date;
userId: string;
amount: import("@prisma/client/runtime/library").Decimal;
direction: string;
date: Date;
memo: string | null;
walletId: string;
recurrenceId: string | null;
}>;
exportCsv(walletId: string, from: string | undefined, to: string | undefined, category: string | undefined, direction: 'in' | 'out' | undefined, res: Response): Promise<void>;
update(walletId: string, id: string, body: unknown): Promise<{
category: string | null;
id: string;
createdAt: Date;
userId: string;
amount: import("@prisma/client/runtime/library").Decimal;
direction: string;
date: Date;
memo: string | null;
walletId: string;
recurrenceId: string | null;
}>;
delete(walletId: string, id: string): Promise<{
category: string | null;
id: string;
createdAt: Date;
userId: string;
amount: import("@prisma/client/runtime/library").Decimal;
direction: string;
date: Date;
memo: string | null;
walletId: string;
recurrenceId: string | null;
}>;
}

View File

@@ -0,0 +1,115 @@
"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.TransactionsController = void 0;
const common_1 = require("@nestjs/common");
const transactions_service_1 = require("./transactions.service");
const transaction_dto_1 = require("./transaction.dto");
let TransactionsController = class TransactionsController {
tx;
constructor(tx) {
this.tx = tx;
}
list(walletId) {
return this.tx.list(walletId);
}
create(walletId, body) {
return this.tx.create(walletId, body);
}
async exportCsv(walletId, from, to, category, direction, res) {
const rows = await this.tx.listWithFilters(walletId, { from, to, category, direction });
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
res.setHeader('Content-Disposition', `attachment; filename="transactions_${walletId}.csv"`);
res.write(`date,category,memo,direction,amount\n`);
const esc = (v) => {
if (v === null || v === undefined)
return '';
const s = String(v);
return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
};
for (const r of rows) {
const line = [
r.date.toISOString(),
esc(r.category ?? ''),
esc(r.memo ?? ''),
r.direction,
r.amount.toString(),
].join(',');
res.write(line + '\n');
}
res.end();
}
async update(walletId, id, body) {
try {
const parsed = transaction_dto_1.TransactionUpdateSchema.parse(body);
return this.tx.update(walletId, id, parsed);
}
catch (e) {
throw new common_1.BadRequestException(e?.errors ?? 'Invalid payload');
}
}
delete(walletId, id) {
return this.tx.delete(walletId, id);
}
};
exports.TransactionsController = TransactionsController;
__decorate([
(0, common_1.Get)(),
__param(0, (0, common_1.Param)('walletId')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", void 0)
], TransactionsController.prototype, "list", null);
__decorate([
(0, common_1.Post)(),
__param(0, (0, common_1.Param)('walletId')),
__param(1, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, Object]),
__metadata("design:returntype", void 0)
], TransactionsController.prototype, "create", null);
__decorate([
(0, common_1.Get)('export.csv'),
__param(0, (0, common_1.Param)('walletId')),
__param(1, (0, common_1.Query)('from')),
__param(2, (0, common_1.Query)('to')),
__param(3, (0, common_1.Query)('category')),
__param(4, (0, common_1.Query)('direction')),
__param(5, (0, common_1.Res)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, Object, Object, Object, Object, Object]),
__metadata("design:returntype", Promise)
], TransactionsController.prototype, "exportCsv", null);
__decorate([
(0, common_1.Put)(':id'),
__param(0, (0, common_1.Param)('walletId')),
__param(1, (0, common_1.Param)('id')),
__param(2, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String, Object]),
__metadata("design:returntype", Promise)
], TransactionsController.prototype, "update", null);
__decorate([
(0, common_1.Delete)(':id'),
__param(0, (0, common_1.Param)('walletId')),
__param(1, (0, common_1.Param)('id')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String]),
__metadata("design:returntype", void 0)
], TransactionsController.prototype, "delete", null);
exports.TransactionsController = TransactionsController = __decorate([
(0, common_1.Controller)('wallets/:walletId/transactions'),
__metadata("design:paramtypes", [transactions_service_1.TransactionsService])
], TransactionsController);
//# sourceMappingURL=transactions.controller.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"transactions.controller.js","sourceRoot":"","sources":["../../src/transactions/transactions.controller.ts"],"names":[],"mappings":"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"}

View File

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

View File

@@ -0,0 +1,25 @@
"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.TransactionsModule = void 0;
const common_1 = require("@nestjs/common");
const transactions_service_1 = require("./transactions.service");
const transactions_controller_1 = require("./transactions.controller");
const prisma_module_1 = require("../prisma/prisma.module");
let TransactionsModule = class TransactionsModule {
};
exports.TransactionsModule = TransactionsModule;
exports.TransactionsModule = TransactionsModule = __decorate([
(0, common_1.Module)({
imports: [prisma_module_1.PrismaModule],
providers: [transactions_service_1.TransactionsService],
controllers: [transactions_controller_1.TransactionsController],
exports: [transactions_service_1.TransactionsService],
})
], TransactionsModule);
//# sourceMappingURL=transactions.module.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"transactions.module.js","sourceRoot":"","sources":["../../src/transactions/transactions.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,iEAA6D;AAC7D,uEAAmE;AACnE,2DAAuD;AAQhD,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;CAAG,CAAA;AAArB,gDAAkB;6BAAlB,kBAAkB;IAN9B,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,4BAAY,CAAC;QACvB,SAAS,EAAE,CAAC,0CAAmB,CAAC;QAChC,WAAW,EAAE,CAAC,gDAAsB,CAAC;QACrC,OAAO,EAAE,CAAC,0CAAmB,CAAC;KAC/B,CAAC;GACW,kBAAkB,CAAG"}

View File

@@ -0,0 +1,91 @@
import { PrismaService } from '../prisma/prisma.service';
import { Prisma } from '@prisma/client';
import type { TransactionUpdateDto } from './transaction.dto';
export declare class TransactionsService {
private prisma;
constructor(prisma: PrismaService);
private userId;
list(walletId: string): Prisma.PrismaPromise<{
category: string | null;
id: string;
createdAt: Date;
userId: string;
amount: Prisma.Decimal;
direction: string;
date: Date;
memo: string | null;
walletId: string;
recurrenceId: string | null;
}[]>;
listAll(): Prisma.PrismaPromise<{
category: string | null;
id: string;
createdAt: Date;
userId: string;
amount: Prisma.Decimal;
direction: string;
date: Date;
memo: string | null;
walletId: string;
recurrenceId: string | null;
}[]>;
listWithFilters(walletId: string, filters: {
from?: string;
to?: string;
category?: string;
direction?: 'in' | 'out';
}): Prisma.PrismaPromise<{
category: string | null;
id: string;
createdAt: Date;
userId: string;
amount: Prisma.Decimal;
direction: string;
date: Date;
memo: string | null;
walletId: string;
recurrenceId: string | null;
}[]>;
create(walletId: string, input: {
amount: string | number;
direction: 'in' | 'out';
date?: string;
category?: string;
memo?: string;
}): Promise<{
category: string | null;
id: string;
createdAt: Date;
userId: string;
amount: Prisma.Decimal;
direction: string;
date: Date;
memo: string | null;
walletId: string;
recurrenceId: string | null;
}>;
update(walletId: string, id: string, dto: TransactionUpdateDto): Promise<{
category: string | null;
id: string;
createdAt: Date;
userId: string;
amount: Prisma.Decimal;
direction: string;
date: Date;
memo: string | null;
walletId: string;
recurrenceId: string | null;
}>;
delete(walletId: string, id: string): Promise<{
category: string | null;
id: string;
createdAt: Date;
userId: string;
amount: Prisma.Decimal;
direction: string;
date: Date;
memo: string | null;
walletId: string;
recurrenceId: string | null;
}>;
}

View File

@@ -0,0 +1,120 @@
"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);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TransactionsService = void 0;
const common_1 = require("@nestjs/common");
const prisma_service_1 = require("../prisma/prisma.service");
const user_util_1 = require("../common/user.util");
let TransactionsService = class TransactionsService {
prisma;
constructor(prisma) {
this.prisma = prisma;
}
userId() {
return (0, user_util_1.getTempUserId)();
}
list(walletId) {
return this.prisma.transaction.findMany({
where: { userId: this.userId(), walletId },
orderBy: { date: 'desc' },
take: 200,
});
}
listAll() {
return this.prisma.transaction.findMany({
where: { userId: this.userId() },
orderBy: { date: 'desc' },
take: 1000,
});
}
listWithFilters(walletId, filters) {
const where = {
userId: (0, user_util_1.getTempUserId)(),
walletId,
};
if (filters.direction)
where.direction = filters.direction;
if (filters.category)
where.category = filters.category;
if (filters.from || filters.to) {
where.date = {};
if (filters.from)
where.date.gte = new Date(filters.from);
if (filters.to)
where.date.lte = new Date(filters.to);
}
return this.prisma.transaction.findMany({
where,
orderBy: { date: 'desc' },
});
}
async create(walletId, input) {
const amountNum = typeof input.amount === 'string' ? Number(input.amount) : input.amount;
if (!Number.isFinite(amountNum))
throw new Error('amount must be a number');
const date = input.date ? new Date(input.date) : new Date();
const wallet = await this.prisma.wallet.findFirst({
where: { id: walletId, userId: this.userId(), deletedAt: null },
select: { id: true },
});
if (!wallet)
throw new Error('wallet not found');
return this.prisma.transaction.create({
data: {
userId: this.userId(),
walletId,
amount: amountNum,
direction: input.direction,
date,
category: input.category ?? null,
memo: input.memo ?? null,
},
});
}
async update(walletId, id, dto) {
const existing = await this.prisma.transaction.findFirst({
where: { id, walletId, userId: this.userId() },
});
if (!existing)
throw new Error('transaction not found');
const data = {};
if (dto.amount !== undefined)
data.amount = Number(dto.amount);
if (dto.direction)
data.direction = dto.direction;
if (dto.category !== undefined)
data.category = dto.category || null;
if (dto.memo !== undefined)
data.memo = dto.memo || null;
if (dto.date !== undefined)
data.date = new Date(dto.date);
return this.prisma.transaction.update({
where: { id: existing.id },
data,
});
}
async delete(walletId, id) {
const existing = await this.prisma.transaction.findFirst({
where: { id, walletId, userId: this.userId() },
});
if (!existing)
throw new Error('transaction not found');
return this.prisma.transaction.delete({
where: { id: existing.id },
});
}
};
exports.TransactionsService = TransactionsService;
exports.TransactionsService = TransactionsService = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [prisma_service_1.PrismaService])
], TransactionsService);
//# sourceMappingURL=transactions.service.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"transactions.service.js","sourceRoot":"","sources":["../../src/transactions/transactions.service.ts"],"names":[],"mappings":"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"}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,16 @@
import { UsersService } from './users.service';
export declare class UsersController {
private readonly users;
constructor(users: UsersService);
me(): Promise<{
id: string;
email: string | null;
createdAt: Date;
updatedAt: Date;
status: string;
name: string | null;
avatarUrl: string | null;
defaultCurrency: string | null;
timeZone: string | null;
} | null>;
}

35
apps/api/dist/users/users.controller.js vendored Normal file
View File

@@ -0,0 +1,35 @@
"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);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UsersController = void 0;
const common_1 = require("@nestjs/common");
const users_service_1 = require("./users.service");
let UsersController = class UsersController {
users;
constructor(users) {
this.users = users;
}
me() {
return this.users.me();
}
};
exports.UsersController = UsersController;
__decorate([
(0, common_1.Get)('me'),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", void 0)
], UsersController.prototype, "me", null);
exports.UsersController = UsersController = __decorate([
(0, common_1.Controller)('users'),
__metadata("design:paramtypes", [users_service_1.UsersService])
], UsersController);
//# sourceMappingURL=users.controller.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"users.controller.js","sourceRoot":"","sources":["../../src/users/users.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAiD;AACjD,mDAA+C;AAGxC,IAAM,eAAe,GAArB,MAAM,eAAe;IACG;IAA7B,YAA6B,KAAmB;QAAnB,UAAK,GAAL,KAAK,CAAc;IAAG,CAAC;IAGpD,EAAE;QACA,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;IACzB,CAAC;CACF,CAAA;AAPY,0CAAe;AAI1B;IADC,IAAA,YAAG,EAAC,IAAI,CAAC;;;;yCAGT;0BANU,eAAe;IAD3B,IAAA,mBAAU,EAAC,OAAO,CAAC;qCAEkB,4BAAY;GADrC,eAAe,CAO3B"}

2
apps/api/dist/users/users.module.d.ts vendored Normal file
View File

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

25
apps/api/dist/users/users.module.js vendored Normal file
View File

@@ -0,0 +1,25 @@
"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.UsersModule = void 0;
const common_1 = require("@nestjs/common");
const users_service_1 = require("./users.service");
const users_controller_1 = require("./users.controller");
const prisma_module_1 = require("../prisma/prisma.module");
let UsersModule = class UsersModule {
};
exports.UsersModule = UsersModule;
exports.UsersModule = UsersModule = __decorate([
(0, common_1.Module)({
imports: [prisma_module_1.PrismaModule],
providers: [users_service_1.UsersService],
controllers: [users_controller_1.UsersController],
exports: [users_service_1.UsersService],
})
], UsersModule);
//# sourceMappingURL=users.module.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"users.module.js","sourceRoot":"","sources":["../../src/users/users.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,mDAA+C;AAC/C,yDAAqD;AACrD,2DAAuD;AAQhD,IAAM,WAAW,GAAjB,MAAM,WAAW;CAAG,CAAA;AAAd,kCAAW;sBAAX,WAAW;IANvB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,4BAAY,CAAC;QACvB,SAAS,EAAE,CAAC,4BAAY,CAAC;QACzB,WAAW,EAAE,CAAC,kCAAe,CAAC;QAC9B,OAAO,EAAE,CAAC,4BAAY,CAAC;KACxB,CAAC;GACW,WAAW,CAAG"}

16
apps/api/dist/users/users.service.d.ts vendored Normal file
View File

@@ -0,0 +1,16 @@
import { PrismaService } from '../prisma/prisma.service';
export declare class UsersService {
private prisma;
constructor(prisma: PrismaService);
me(): Promise<{
id: string;
email: string | null;
createdAt: Date;
updatedAt: Date;
status: string;
name: string | null;
avatarUrl: string | null;
defaultCurrency: string | null;
timeZone: string | null;
} | null>;
}

31
apps/api/dist/users/users.service.js vendored Normal file
View File

@@ -0,0 +1,31 @@
"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);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UsersService = void 0;
const common_1 = require("@nestjs/common");
const prisma_service_1 = require("../prisma/prisma.service");
const user_util_1 = require("../common/user.util");
let UsersService = class UsersService {
prisma;
constructor(prisma) {
this.prisma = prisma;
}
async me() {
const userId = (0, user_util_1.getTempUserId)();
return this.prisma.user.findUnique({ where: { id: userId } });
}
};
exports.UsersService = UsersService;
exports.UsersService = UsersService = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [prisma_service_1.PrismaService])
], UsersService);
//# sourceMappingURL=users.service.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"users.service.js","sourceRoot":"","sources":["../../src/users/users.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,6DAAyD;AACzD,mDAAoD;AAG7C,IAAM,YAAY,GAAlB,MAAM,YAAY;IACH;IAApB,YAAoB,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAE7C,KAAK,CAAC,EAAE;QACN,MAAM,MAAM,GAAG,IAAA,yBAAa,GAAE,CAAC;QAC/B,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;IAChE,CAAC;CACF,CAAA;AAPY,oCAAY;uBAAZ,YAAY;IADxB,IAAA,mBAAU,GAAE;qCAEiB,8BAAa;GAD9B,YAAY,CAOxB"}

View File

@@ -0,0 +1,87 @@
import { WalletsService } from './wallets.service';
import { TransactionsService } from '../transactions/transactions.service';
export declare class WalletsController {
private readonly wallets;
private readonly transactions;
constructor(wallets: WalletsService, transactions: TransactionsService);
list(): import("@prisma/client").Prisma.PrismaPromise<{
id: string;
createdAt: Date;
updatedAt: Date;
name: string;
userId: string;
kind: string;
currency: string | null;
unit: string | null;
initialAmount: import("@prisma/client/runtime/library").Decimal | null;
pricePerUnit: import("@prisma/client/runtime/library").Decimal | null;
deletedAt: Date | null;
}[]>;
getAllTransactions(): Promise<{
category: string | null;
id: string;
createdAt: Date;
userId: string;
amount: import("@prisma/client/runtime/library").Decimal;
direction: string;
date: Date;
memo: string | null;
walletId: string;
recurrenceId: string | null;
}[]>;
create(body: {
name: string;
currency?: string;
kind?: 'money' | 'asset';
unit?: string;
initialAmount?: number;
pricePerUnit?: number;
}): import("@prisma/client").Prisma.Prisma__WalletClient<{
id: string;
createdAt: Date;
updatedAt: Date;
name: string;
userId: string;
kind: string;
currency: string | null;
unit: string | null;
initialAmount: import("@prisma/client/runtime/library").Decimal | null;
pricePerUnit: import("@prisma/client/runtime/library").Decimal | null;
deletedAt: Date | null;
}, never, import("@prisma/client/runtime/library").DefaultArgs, import("@prisma/client").Prisma.PrismaClientOptions> | {
error: string;
};
update(id: string, body: {
name?: string;
currency?: string;
kind?: 'money' | 'asset';
unit?: string;
initialAmount?: number;
pricePerUnit?: number;
}): import("@prisma/client").Prisma.Prisma__WalletClient<{
id: string;
createdAt: Date;
updatedAt: Date;
name: string;
userId: string;
kind: string;
currency: string | null;
unit: string | null;
initialAmount: import("@prisma/client/runtime/library").Decimal | null;
pricePerUnit: import("@prisma/client/runtime/library").Decimal | null;
deletedAt: Date | null;
}, never, import("@prisma/client/runtime/library").DefaultArgs, import("@prisma/client").Prisma.PrismaClientOptions>;
delete(id: string): import("@prisma/client").Prisma.Prisma__WalletClient<{
id: string;
createdAt: Date;
updatedAt: Date;
name: string;
userId: string;
kind: string;
currency: string | null;
unit: string | null;
initialAmount: import("@prisma/client/runtime/library").Decimal | null;
pricePerUnit: import("@prisma/client/runtime/library").Decimal | null;
deletedAt: Date | null;
}, never, import("@prisma/client/runtime/library").DefaultArgs, import("@prisma/client").Prisma.PrismaClientOptions>;
}

View File

@@ -0,0 +1,85 @@
"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.WalletsController = void 0;
const common_1 = require("@nestjs/common");
const wallets_service_1 = require("./wallets.service");
const transactions_service_1 = require("../transactions/transactions.service");
let WalletsController = class WalletsController {
wallets;
transactions;
constructor(wallets, transactions) {
this.wallets = wallets;
this.transactions = transactions;
}
list() {
return this.wallets.list();
}
async getAllTransactions() {
return this.transactions.listAll();
}
create(body) {
if (!body?.name) {
return { error: 'name is required' };
}
return this.wallets.create(body);
}
update(id, body) {
return this.wallets.update(id, body);
}
delete(id) {
return this.wallets.delete(id);
}
};
exports.WalletsController = WalletsController;
__decorate([
(0, common_1.Get)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", void 0)
], WalletsController.prototype, "list", null);
__decorate([
(0, common_1.Get)('transactions'),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], WalletsController.prototype, "getAllTransactions", null);
__decorate([
(0, common_1.Post)(),
__param(0, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", void 0)
], WalletsController.prototype, "create", null);
__decorate([
(0, common_1.Put)(':id'),
__param(0, (0, common_1.Param)('id')),
__param(1, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, Object]),
__metadata("design:returntype", void 0)
], WalletsController.prototype, "update", null);
__decorate([
(0, common_1.Delete)(':id'),
__param(0, (0, common_1.Param)('id')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", void 0)
], WalletsController.prototype, "delete", null);
exports.WalletsController = WalletsController = __decorate([
(0, common_1.Controller)('wallets'),
__metadata("design:paramtypes", [wallets_service_1.WalletsService,
transactions_service_1.TransactionsService])
], WalletsController);
//# sourceMappingURL=wallets.controller.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"wallets.controller.js","sourceRoot":"","sources":["../../src/wallets/wallets.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAiF;AACjF,uDAAmD;AACnD,+EAA2E;AAGpE,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAET;IACA;IAFnB,YACmB,OAAuB,EACvB,YAAiC;QADjC,YAAO,GAAP,OAAO,CAAgB;QACvB,iBAAY,GAAZ,YAAY,CAAqB;IACjD,CAAC;IAGJ,IAAI;QACF,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IAC7B,CAAC;IAGK,AAAN,KAAK,CAAC,kBAAkB;QACtB,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;IACrC,CAAC;IAGD,MAAM,CAAS,IAAiI;QAC9I,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;YAChB,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC;QACvC,CAAC;QACD,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAGD,MAAM,CAAc,EAAU,EAAU,IAAkI;QACxK,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACvC,CAAC;IAGD,MAAM,CAAc,EAAU;QAC5B,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC;CACF,CAAA;AAjCY,8CAAiB;AAO5B;IADC,IAAA,YAAG,GAAE;;;;6CAGL;AAGK;IADL,IAAA,YAAG,EAAC,cAAc,CAAC;;;;2DAGnB;AAGD;IADC,IAAA,aAAI,GAAE;IACC,WAAA,IAAA,aAAI,GAAE,CAAA;;;;+CAKb;AAGD;IADC,IAAA,YAAG,EAAC,KAAK,CAAC;IACH,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;IAAc,WAAA,IAAA,aAAI,GAAE,CAAA;;;;+CAEtC;AAGD;IADC,IAAA,eAAM,EAAC,KAAK,CAAC;IACN,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;+CAElB;4BAhCU,iBAAiB;IAD7B,IAAA,mBAAU,EAAC,SAAS,CAAC;qCAGQ,gCAAc;QACT,0CAAmB;GAHzC,iBAAiB,CAiC7B"}

View File

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

26
apps/api/dist/wallets/wallets.module.js vendored Normal file
View File

@@ -0,0 +1,26 @@
"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.WalletsModule = void 0;
const common_1 = require("@nestjs/common");
const wallets_service_1 = require("./wallets.service");
const wallets_controller_1 = require("./wallets.controller");
const transactions_service_1 = require("../transactions/transactions.service");
const prisma_module_1 = require("../prisma/prisma.module");
let WalletsModule = class WalletsModule {
};
exports.WalletsModule = WalletsModule;
exports.WalletsModule = WalletsModule = __decorate([
(0, common_1.Module)({
imports: [prisma_module_1.PrismaModule],
providers: [wallets_service_1.WalletsService, transactions_service_1.TransactionsService],
controllers: [wallets_controller_1.WalletsController],
exports: [wallets_service_1.WalletsService],
})
], WalletsModule);
//# sourceMappingURL=wallets.module.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"wallets.module.js","sourceRoot":"","sources":["../../src/wallets/wallets.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,uDAAmD;AACnD,6DAAyD;AACzD,+EAA2E;AAC3E,2DAAuD;AAQhD,IAAM,aAAa,GAAnB,MAAM,aAAa;CAAG,CAAA;AAAhB,sCAAa;wBAAb,aAAa;IANzB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,4BAAY,CAAC;QACvB,SAAS,EAAE,CAAC,gCAAc,EAAE,0CAAmB,CAAC;QAChD,WAAW,EAAE,CAAC,sCAAiB,CAAC;QAChC,OAAO,EAAE,CAAC,gCAAc,CAAC;KAC1B,CAAC;GACW,aAAa,CAAG"}

View File

@@ -0,0 +1,72 @@
import { PrismaService } from '../prisma/prisma.service';
export declare class WalletsService {
private prisma;
constructor(prisma: PrismaService);
private userId;
list(): import("@prisma/client").Prisma.PrismaPromise<{
id: string;
createdAt: Date;
updatedAt: Date;
name: string;
userId: string;
kind: string;
currency: string | null;
unit: string | null;
initialAmount: import("@prisma/client/runtime/library").Decimal | null;
pricePerUnit: import("@prisma/client/runtime/library").Decimal | null;
deletedAt: Date | null;
}[]>;
create(input: {
name: string;
currency?: string;
kind?: 'money' | 'asset';
unit?: string;
initialAmount?: number;
pricePerUnit?: number;
}): import("@prisma/client").Prisma.Prisma__WalletClient<{
id: string;
createdAt: Date;
updatedAt: Date;
name: string;
userId: string;
kind: string;
currency: string | null;
unit: string | null;
initialAmount: import("@prisma/client/runtime/library").Decimal | null;
pricePerUnit: import("@prisma/client/runtime/library").Decimal | null;
deletedAt: Date | null;
}, never, import("@prisma/client/runtime/library").DefaultArgs, import("@prisma/client").Prisma.PrismaClientOptions>;
update(id: string, input: {
name?: string;
currency?: string;
kind?: 'money' | 'asset';
unit?: string;
initialAmount?: number;
pricePerUnit?: number;
}): import("@prisma/client").Prisma.Prisma__WalletClient<{
id: string;
createdAt: Date;
updatedAt: Date;
name: string;
userId: string;
kind: string;
currency: string | null;
unit: string | null;
initialAmount: import("@prisma/client/runtime/library").Decimal | null;
pricePerUnit: import("@prisma/client/runtime/library").Decimal | null;
deletedAt: Date | null;
}, never, import("@prisma/client/runtime/library").DefaultArgs, import("@prisma/client").Prisma.PrismaClientOptions>;
delete(id: string): import("@prisma/client").Prisma.Prisma__WalletClient<{
id: string;
createdAt: Date;
updatedAt: Date;
name: string;
userId: string;
kind: string;
currency: string | null;
unit: string | null;
initialAmount: import("@prisma/client/runtime/library").Decimal | null;
pricePerUnit: import("@prisma/client/runtime/library").Decimal | null;
deletedAt: Date | null;
}, never, import("@prisma/client/runtime/library").DefaultArgs, import("@prisma/client").Prisma.PrismaClientOptions>;
}

View File

@@ -0,0 +1,86 @@
"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);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WalletsService = void 0;
const common_1 = require("@nestjs/common");
const prisma_service_1 = require("../prisma/prisma.service");
const user_util_1 = require("../common/user.util");
let WalletsService = class WalletsService {
prisma;
constructor(prisma) {
this.prisma = prisma;
}
userId() {
return (0, user_util_1.getTempUserId)();
}
list() {
return this.prisma.wallet.findMany({
where: { userId: this.userId(), deletedAt: null },
orderBy: { createdAt: 'asc' },
});
}
create(input) {
const kind = input.kind ?? 'money';
return this.prisma.wallet.create({
data: {
userId: this.userId(),
name: input.name,
kind,
currency: kind === 'money' ? (input.currency ?? 'IDR') : null,
unit: kind === 'asset' ? (input.unit ?? null) : null,
initialAmount: input.initialAmount || null,
pricePerUnit: kind === 'asset' ? (input.pricePerUnit || null) : null,
},
});
}
update(id, input) {
const updateData = {};
if (input.name !== undefined)
updateData.name = input.name;
if (input.kind !== undefined) {
updateData.kind = input.kind;
if (input.kind === 'money') {
updateData.currency = input.currency ?? 'IDR';
updateData.unit = null;
}
else {
updateData.unit = input.unit ?? null;
updateData.currency = null;
}
}
else {
if (input.currency !== undefined)
updateData.currency = input.currency;
if (input.unit !== undefined)
updateData.unit = input.unit;
}
if (input.initialAmount !== undefined)
updateData.initialAmount = input.initialAmount || null;
if (input.pricePerUnit !== undefined)
updateData.pricePerUnit = input.pricePerUnit || null;
return this.prisma.wallet.update({
where: { id, userId: this.userId() },
data: updateData,
});
}
delete(id) {
return this.prisma.wallet.update({
where: { id, userId: this.userId() },
data: { deletedAt: new Date() },
});
}
};
exports.WalletsService = WalletsService;
exports.WalletsService = WalletsService = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [prisma_service_1.PrismaService])
], WalletsService);
//# sourceMappingURL=wallets.service.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"wallets.service.js","sourceRoot":"","sources":["../../src/wallets/wallets.service.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,34 @@
// @ts-check
import eslint from '@eslint/js';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
import globals from 'globals';
import tseslint from 'typescript-eslint';
export default tseslint.config(
{
ignores: ['eslint.config.mjs'],
},
eslint.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
eslintPluginPrettierRecommended,
{
languageOptions: {
globals: {
...globals.node,
...globals.jest,
},
sourceType: 'commonjs',
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
},
{
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-floating-promises': 'warn',
'@typescript-eslint/no-unsafe-argument': 'warn'
},
},
);

8
apps/api/nest-cli.json Normal file
View File

@@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}

12272
apps/api/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

87
apps/api/package.json Normal file
View File

@@ -0,0 +1,87 @@
{
"name": "api",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json",
"dev": "nest start --watch",
"prisma:generate": "prisma generate",
"postinstall": "prisma generate",
"seed": "ts-node src/seed.ts"
},
"dependencies": {
"@nestjs/common": "^11.0.1",
"@nestjs/config": "^4.0.2",
"@nestjs/core": "^11.0.1",
"@nestjs/platform-express": "^11.0.1",
"@nestjs/swagger": "^11.2.0",
"@prisma/client": "^6.17.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.2",
"cookie-parser": "^1.4.7",
"firebase-admin": "^13.5.0",
"jose": "^6.0.12",
"pg": "^8.16.3",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1",
"swagger-ui-express": "^5.0.1",
"zod": "^4.0.17"
},
"devDependencies": {
"@eslint/eslintrc": "^3.2.0",
"@eslint/js": "^9.18.0",
"@nestjs/cli": "^11.0.10",
"@nestjs/schematics": "^11.0.0",
"@nestjs/testing": "^11.0.1",
"@types/express": "^5.0.0",
"@types/jest": "^30.0.0",
"@types/node": "^22.10.7",
"@types/supertest": "^6.0.2",
"eslint": "^9.18.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-prettier": "^5.2.2",
"globals": "^16.0.0",
"jest": "^30.0.0",
"prettier": "^3.4.2",
"prisma": "^6.14.0",
"source-map-support": "^0.5.21",
"supertest": "^7.0.0",
"ts-jest": "^29.2.5",
"ts-loader": "^9.5.2",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.7.3",
"typescript-eslint": "^8.20.0"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}

View File

@@ -0,0 +1,141 @@
-- CreateTable
CREATE TABLE "public"."User" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"status" TEXT NOT NULL DEFAULT 'active',
"email" TEXT,
"name" TEXT,
"avatarUrl" TEXT,
"defaultCurrency" TEXT,
"timeZone" TEXT,
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "public"."AuthAccount" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"provider" TEXT NOT NULL,
"issuer" TEXT NOT NULL,
"subject" TEXT NOT NULL,
"lastLogin" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "AuthAccount_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "public"."Session" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"expiresAt" TIMESTAMP(3) NOT NULL,
"ip" TEXT,
"userAgent" TEXT,
"revokedAt" TIMESTAMP(3),
CONSTRAINT "Session_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "public"."Wallet" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"kind" TEXT NOT NULL,
"name" TEXT NOT NULL,
"currency" TEXT,
"unit" TEXT,
"deletedAt" TIMESTAMP(3),
CONSTRAINT "Wallet_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "public"."Transaction" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"walletId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"date" TIMESTAMP(3) NOT NULL,
"amount" DECIMAL(18,2) NOT NULL,
"direction" TEXT NOT NULL,
"category" TEXT,
"memo" TEXT,
"recurrenceId" TEXT,
CONSTRAINT "Transaction_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "public"."Recurrence" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"rule" TEXT NOT NULL,
"nextRunAt" TIMESTAMP(3) NOT NULL,
"lastRunAt" TIMESTAMP(3),
"idempotency" TEXT NOT NULL,
CONSTRAINT "Recurrence_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "public"."CurrencyRate" (
"id" TEXT NOT NULL,
"base" TEXT NOT NULL,
"quote" TEXT NOT NULL,
"at" TIMESTAMP(3) NOT NULL,
"rate" DECIMAL(18,6) NOT NULL,
CONSTRAINT "CurrencyRate_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "public"."User"("email");
-- CreateIndex
CREATE INDEX "AuthAccount_userId_idx" ON "public"."AuthAccount"("userId");
-- CreateIndex
CREATE UNIQUE INDEX "AuthAccount_issuer_subject_key" ON "public"."AuthAccount"("issuer", "subject");
-- CreateIndex
CREATE INDEX "Session_userId_idx" ON "public"."Session"("userId");
-- CreateIndex
CREATE INDEX "Wallet_userId_idx" ON "public"."Wallet"("userId");
-- CreateIndex
CREATE INDEX "Transaction_userId_walletId_date_idx" ON "public"."Transaction"("userId", "walletId", "date");
-- CreateIndex
CREATE UNIQUE INDEX "Recurrence_idempotency_key" ON "public"."Recurrence"("idempotency");
-- CreateIndex
CREATE INDEX "Recurrence_userId_idx" ON "public"."Recurrence"("userId");
-- CreateIndex
CREATE INDEX "CurrencyRate_base_quote_idx" ON "public"."CurrencyRate"("base", "quote");
-- CreateIndex
CREATE UNIQUE INDEX "CurrencyRate_base_quote_at_key" ON "public"."CurrencyRate"("base", "quote", "at");
-- AddForeignKey
ALTER TABLE "public"."AuthAccount" ADD CONSTRAINT "AuthAccount_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "public"."Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "public"."Wallet" ADD CONSTRAINT "Wallet_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "public"."Transaction" ADD CONSTRAINT "Transaction_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "public"."Transaction" ADD CONSTRAINT "Transaction_walletId_fkey" FOREIGN KEY ("walletId") REFERENCES "public"."Wallet"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "public"."Recurrence" ADD CONSTRAINT "Recurrence_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider = "postgresql"

View File

@@ -0,0 +1,123 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
shadowDatabaseUrl = env("SHADOW_DATABASE_URL")
}
model User {
id String @id @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
status String @default("active")
email String? @unique
name String?
avatarUrl String?
defaultCurrency String?
timeZone String?
authAccounts AuthAccount[]
categories Category[]
Recurrence Recurrence[]
sessions Session[]
transactions Transaction[]
wallets Wallet[]
}
model AuthAccount {
id String @id @default(uuid())
userId String
provider String
issuer String
subject String
lastLogin DateTime @default(now())
user User @relation(fields: [userId], references: [id])
@@unique([issuer, subject])
@@index([userId])
}
model Session {
id String @id @default(uuid())
userId String
createdAt DateTime @default(now())
expiresAt DateTime
ip String?
userAgent String?
revokedAt DateTime?
user User @relation(fields: [userId], references: [id])
@@index([userId])
}
model Wallet {
id String @id @default(uuid())
userId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
kind String
name String
currency String?
unit String?
initialAmount Decimal? @db.Decimal(18, 2)
pricePerUnit Decimal? @db.Decimal(18, 2)
deletedAt DateTime?
transactions Transaction[]
user User @relation(fields: [userId], references: [id])
@@index([userId])
}
model Category {
id String @id @default(uuid())
userId String
name String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
@@unique([userId, name])
@@index([userId])
}
model Transaction {
id String @id @default(uuid())
userId String
walletId String
createdAt DateTime @default(now())
date DateTime
amount Decimal @db.Decimal(18, 2)
direction String
category String?
memo String?
recurrenceId String?
user User @relation(fields: [userId], references: [id])
wallet Wallet @relation(fields: [walletId], references: [id])
@@index([userId, walletId, date])
}
model Recurrence {
id String @id @default(uuid())
userId String
rule String
nextRunAt DateTime
lastRunAt DateTime?
idempotency String @unique
user User @relation(fields: [userId], references: [id])
@@index([userId])
}
model CurrencyRate {
id String @id @default(uuid())
base String
quote String
at DateTime
rate Decimal @db.Decimal(18, 6)
@@unique([base, quote, at])
@@index([base, quote])
}

View File

@@ -0,0 +1,22 @@
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get<AppController>(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});

View File

@@ -0,0 +1,12 @@
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}

View File

@@ -0,0 +1,31 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import * as path from 'path';
import { PrismaModule } from './prisma/prisma.module';
import { AuthModule } from './auth/auth.module';
import { HealthController } from './health/health.controller';
import { UsersModule } from './users/users.module';
import { WalletsModule } from './wallets/wallets.module';
import { TransactionsModule } from './transactions/transactions.module';
import { CategoriesModule } from './categories/categories.module';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: [
path.resolve(process.cwd(), '.env'),
path.resolve(process.cwd(), '../../.env'),
],
}),
PrismaModule,
AuthModule,
UsersModule,
WalletsModule,
TransactionsModule,
CategoriesModule,
],
controllers: [HealthController],
providers: [],
})
export class AppModule {}

View File

@@ -0,0 +1,8 @@
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}

View File

@@ -0,0 +1,36 @@
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { FirebaseService } from './firebase.service';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private firebaseService: FirebaseService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
// If Firebase is not configured, allow all requests (development mode)
if (!this.firebaseService.isFirebaseConfigured()) {
console.warn('⚠️ Firebase not configured - allowing request without auth');
return true;
}
const token = this.extractTokenFromHeader(request);
if (!token) {
throw new UnauthorizedException('No token provided');
}
try {
const decodedToken = await this.firebaseService.verifyIdToken(token);
request.user = decodedToken;
return true;
} catch (error) {
throw new UnauthorizedException('Invalid token');
}
}
private extractTokenFromHeader(request: any): string | undefined {
const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
}
}

View File

@@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { FirebaseService } from './firebase.service';
import { AuthGuard } from './auth.guard';
@Module({
providers: [FirebaseService, AuthGuard],
exports: [FirebaseService, AuthGuard],
})
export class AuthModule {}

View File

@@ -0,0 +1,65 @@
import { Injectable } from '@nestjs/common';
import * as admin from 'firebase-admin';
@Injectable()
export class FirebaseService {
private app: admin.app.App | null = null;
private isConfigured: boolean = false;
constructor() {
// Only initialize Firebase if credentials are available
const projectId = process.env.FIREBASE_PROJECT_ID;
const clientEmail = process.env.FIREBASE_CLIENT_EMAIL;
const privateKey = process.env.FIREBASE_PRIVATE_KEY;
if (projectId && clientEmail && privateKey) {
try {
if (!admin.apps.length) {
this.app = admin.initializeApp({
credential: admin.credential.cert({
projectId,
clientEmail,
privateKey: privateKey.replace(/\\n/g, '\n'),
}),
});
} else {
this.app = admin.app();
}
this.isConfigured = true;
console.log('✅ Firebase Admin initialized successfully');
} catch (error) {
console.warn('⚠️ Firebase Admin initialization failed:', error.message);
this.isConfigured = false;
}
} else {
console.warn('⚠️ Firebase credentials not found. Auth will use fallback mode.');
this.isConfigured = false;
}
}
async verifyIdToken(idToken: string): Promise<admin.auth.DecodedIdToken> {
if (!this.isConfigured || !this.app) {
throw new Error('Firebase not configured');
}
try {
return await admin.auth().verifyIdToken(idToken);
} catch (error) {
throw new Error('Invalid token');
}
}
async getUser(uid: string): Promise<admin.auth.UserRecord> {
if (!this.isConfigured || !this.app) {
throw new Error('Firebase not configured');
}
try {
return await admin.auth().getUser(uid);
} catch (error) {
throw new Error('User not found');
}
}
isFirebaseConfigured(): boolean {
return this.isConfigured;
}
}

View File

@@ -0,0 +1,38 @@
import {
Controller,
Get,
Post,
Body,
Param,
Delete,
} from '@nestjs/common';
import { CategoriesService } from '../categories/categories.service';
import { CreateCategoryDto } from '../categories/dto/create-category.dto';
import { getTempUserId } from '../common/user.util';
@Controller('categories')
export class CategoriesController {
constructor(private readonly categoriesService: CategoriesService) {}
private userId(): string {
return getTempUserId();
}
@Post()
create(@Body() createCategoryDto: CreateCategoryDto) {
return this.categoriesService.create({
...createCategoryDto,
userId: this.userId(),
});
}
@Get()
findAll() {
return this.categoriesService.findAll(this.userId());
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.categoriesService.remove(id, this.userId());
}
}

View File

@@ -0,0 +1,13 @@
import { Module } from '@nestjs/common';
import { CategoriesService } from './categories.service';
import { CategoriesController } from './categories.controller';
import { PrismaModule } from '../prisma/prisma.module';
import { AuthModule } from '../auth/auth.module';
@Module({
imports: [PrismaModule, AuthModule],
controllers: [CategoriesController],
providers: [CategoriesService],
exports: [CategoriesService],
})
export class CategoriesModule {}

Some files were not shown because too many files have changed in this diff Show More