Skip to content

admin-api (Backend)

Backend API gateway untuk Naluri Admin Panel.


Source Code References

Untuk Claude: Gunakan path ini untuk akses langsung ke source code.

ItemPath
Repository/Users/joko/Documents/projects/admin-api
Coupons Modulesrc/coupons/
Coupons Controllersrc/coupons/coupons.controller.ts
Coupons Entitysrc/coupons/entities/coupons.entity.ts
Focus Dashboardsrc/focus-dashboard/
Focus Dashboard Servicesrc/focus-dashboard/focus-dashboard.service.ts

Tech Stack

TechnologyVersionPurpose
NestJS9.xFramework
TypeORM0.3.xORM 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 management

Database 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

VariableDescription
NALURI_DB_*Naluri Core database connection
INTERNAL_ENTITLEMENT_HOSTentitlement-service URL
ENTITLEMENT_HOSTAlternative entitlement URL

Key Modules

Coupons Module

Path: /coupon/*

EndpointMethodDescription
/coupon/listPOSTList coupons with pagination
/coupon/show/{code}GETGet coupon by code
/coupon/show/id/{id}GETGet coupon by ID
/coupon/update-end-datePUTUpdate trial end date
/coupon/dependent-sponsor-code/{id}GET/PUTManage dependent codes
/coupon/{code}/domain-whitelistsGET/PUTManage domain whitelist
/coupon/{id}/blood-test-availabilityGET/POST/PUT/DELETEBlood test periods
/coupon/{id}/blood-test-locationsGET/POST/PUT/DELETEBlood test locations

Focus Dashboard Module

Path: /focus-dashboard/*

Acts as proxy to entitlement-service.

EndpointMethodProxies To
/focus-dashboard/details/{sponsorCodeId}GET/analytics/focus-dashboard/{id}
/focus-dashboard/search-usersGET/analytics/search-users
/focus-dashboard/manage-accessPOST/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

Internal Documentation