Appearance
admin-api (Backend)
Backend API gateway untuk Naluri Admin Panel.
Source Code References
Untuk Claude: Gunakan path ini untuk akses langsung ke source code.
| Item | Path |
|---|---|
| Repository | /Users/joko/Documents/projects/admin-api |
| Coupons Module | src/coupons/ |
| Coupons Controller | src/coupons/coupons.controller.ts |
| Coupons Entity | src/coupons/entities/coupons.entity.ts |
| Focus Dashboard | src/focus-dashboard/ |
| Focus Dashboard Service | src/focus-dashboard/focus-dashboard.service.ts |
Tech Stack
| Technology | Version | Purpose |
|---|---|---|
| NestJS | 9.x | Framework |
| TypeORM | 0.3.x | ORM untuk Naluri Core DB |
| PostgreSQL | - | Database |
| Passport | - | Authentication |
| JWT | - | Token-based auth |
| Axios | - | HTTP client untuk external services |
| Cache Manager | - | Caching |
Project Structure
src/
├── access/ # Access control
├── acl/ # Role-based Access Control
├── audit/ # Audit logging
├── auth/ # JWT authentication
├── coach/ # Coach management
├── coupons/ # Coupon management (main)
│ ├── coupons.controller.ts
│ ├── coupons.service.ts
│ ├── entities/ # TypeORM entities
│ │ ├── coupons.entity.ts
│ │ ├── coupon-code.entity.ts
│ │ └── ...
│ └── services/ # Sub-services
│ ├── coupon-read.service.ts
│ ├── coupon-edit.service.ts
│ ├── dependent-coupon-code.service.ts
│ └── domain-whitelist.service.ts
├── features/ # Feature flags
├── focus-dashboard/ # Focus Dashboard proxy
│ ├── focus-dashboard.controller.ts
│ ├── focus-dashboard.service.ts
│ └── dto/
├── groups/ # User groups
├── members/ # Member management
├── plans/ # Subscription plans
├── subscriptions/ # Subscription management
├── upload/ # File upload (S3)
└── users/ # User managementDatabase Connections
Naluri Core DB
typescript
// TypeORM connection
@Entity()
export class Coupons {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
title: string;
@Column()
sponsor: string;
@Column()
code: string;
// ...
}Environment Variables
| Variable | Description |
|---|---|
NALURI_DB_* | Naluri Core database connection |
INTERNAL_ENTITLEMENT_HOST | entitlement-service URL |
ENTITLEMENT_HOST | Alternative entitlement URL |
Key Modules
Coupons Module
Path: /coupon/*
| Endpoint | Method | Description |
|---|---|---|
/coupon/list | POST | List coupons with pagination |
/coupon/show/{code} | GET | Get coupon by code |
/coupon/show/id/{id} | GET | Get coupon by ID |
/coupon/update-end-date | PUT | Update trial end date |
/coupon/dependent-sponsor-code/{id} | GET/PUT | Manage dependent codes |
/coupon/{code}/domain-whitelists | GET/PUT | Manage domain whitelist |
/coupon/{id}/blood-test-availability | GET/POST/PUT/DELETE | Blood test periods |
/coupon/{id}/blood-test-locations | GET/POST/PUT/DELETE | Blood test locations |
Focus Dashboard Module
Path: /focus-dashboard/*
Acts as proxy to entitlement-service.
| Endpoint | Method | Proxies To |
|---|---|---|
/focus-dashboard/details/{sponsorCodeId} | GET | /analytics/focus-dashboard/{id} |
/focus-dashboard/search-users | GET | /analytics/search-users |
/focus-dashboard/manage-access | POST | /analytics/manage-access |
Service Patterns
Direct Database Access (Coupons)
typescript
// src/coupons/services/coupon-read.service.ts
export class CouponReadService {
constructor(
private readonly naluriDataSource: DataSource,
private readonly cacheManager: Cache,
) {}
async get(id: string) {
return await this.naluriDataSource.getRepository(Coupons).findOne({
where: { id },
relations: {
couponCodes: true,
plan: { subscriptions: { member: true } },
},
});
}
}Proxy to Entitlement (Focus Dashboard)
typescript
// src/focus-dashboard/focus-dashboard.service.ts
@Injectable()
export class FocusDashboardService {
constructor(private readonly httpService: HttpService) {}
async getFocusDashboardDetails(sponsorCodeId: string) {
const $observable = this.httpService
.get(`/analytics/focus-dashboard/${sponsorCodeId}`)
.pipe(map((res) => res.data));
return await lastValueFrom($observable);
}
}Proxy to Entitlement (Other Services)
typescript
// src/coupons/services/dependent-coupon-code.service.ts
const entitlementHost = this.configService.get<string>('ENTITLEMENT_HOST');
const response = await this.httpService.get(
`${entitlementHost}/dependent-sponsor-code/${id}`
);Entities
Coupons
typescript
// src/coupons/entities/coupons.entity.ts
@Entity()
export class Coupons {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
title: string;
@Column()
sponsor: string;
@Column()
code: string;
@Column()
active: boolean;
@Column({ name: 'plan_id', type: 'uuid', nullable: true })
planId: string;
@OneToMany(() => CouponCodes, couponCode => couponCode.coupon)
couponCodes: CouponCodes[];
@ManyToOne(() => Plans, plan => plan.coupons)
@JoinColumn({ name: 'plan_id' })
plan: Plans;
}CouponCodes
typescript
// src/coupons/entities/coupon-code.entity.ts
@Entity()
export class CouponCodes {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
name: string;
@Column({ name: 'coupon_id', type: 'uuid', nullable: true })
couponId: string;
@ManyToOne(() => Coupons, coupon => coupon.couponCodes)
@JoinColumn({ name: 'coupon_id' })
coupon: Coupons;
}Module Configuration
Focus Dashboard Module
typescript
// src/focus-dashboard/focus-dashboard.module.ts
@Module({
imports: [
HttpModule.registerAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
baseURL: config.get<string>('INTERNAL_ENTITLEMENT_HOST'),
timeout: 3000,
headers: {
'User-Agent': 'Naluri Admin API',
'Content-Type': 'application/json',
},
}),
}),
],
controllers: [FocusDashboardController],
providers: [FocusDashboardService],
})
export class FocusDashboardModule {}Last updated: March 2026