---
# System prepended metadata

title: '[KB] Knowledgebase ROOV'

---

# [KB] Knowledgebase ROOV

## Radioplus repository development cycle


### Development

```plantuml
actor Developer as dev
actor Reviewer as rev
participant Bitbucket as bitbucket
participant Gitlab as gitlab
participant "Dev environment" as devenv

note over dev
origin:
git@bitbucket.org:rctiplus/radioplus-webssr.git
end note


note over rev
origin:
git@bitbucket.org:rctiplus/radioplus-webssr.git
end note

dev ->o bitbucket: Push to "development"

rev --> rev: Change remote origin url to "gitlab"
note right
git remote set-url origin https://mnc-repo.mncdigital.com/roov/radioplus.git
end note

note over rev
origin:
https://mnc-repo.mncdigital.com/roov/radioplus.git
end note

rev --> rev: Fetch origin "development"

note right
git fetch origin development
end note

rev --> gitlab: Force push to development

note right
git push -f origin development
end note

gitlab --> devenv: Deployment to Dev (CI/CD)

rev --> rev: Revert back remote origin url to "bitbucket"

note right
git remote set-url origin git@bitbucket.org:rctiplus/radioplus-webssr.git
end note

note over rev
origin:
git@bitbucket.org:rctiplus/radioplus-webssr.git
end note

```

```plantuml
@startuml

note "moderationStatusID = {\n\tPENDING: 1\n\tAPPROVE: 2\n\tREJECT: 3\n}" as moderationStatus

PENDING: 1
APPROVE: 2
REJECT: 3

[*] --> PENDING

PENDING --> APPROVE
PENDING --> REJECT

APPROVE --> PENDING
REJECT --> PENDING
```

## Auth Client Scheme
```plantuml
actor Client as client
participant AuthAPI as auth

group Authenticate client

client -> auth: Authenticate client `GET /clients`

note right
Headers: {
    "Authrorization": "Basic ${CLIENT_ID}:${CLIENT_SECRET}"
}
end note

auth --> client: Authenticate client responses`

note right
Headers: `X-Client-Token, X-Client-Token-Expired-At
end note

end

group Authenticate session

client -> auth: Authenticate sessions: `POST /sessions`

note right
Headers: {
    "Authrorization": "Bearer ${X-Client-Token}"
}
end note

auth --> client: Authenticate session responses

note right
Headers: `X-Access-Token, X-Access-Token-Expired-At, X-Refresh-Access-Token, X-Refresh-Access-Token-Expired-At`
end note

end

group Refresh session

client -> client: X-Acces-Token expired

else Refresh session not expired

client -> auth: Refresh sessions: `PUT /sessions`

note right
Headers: {
    "Authrorization": "Bearer ${X-Refresh-Access-Token}"
}
end note

auth --> client: Authenticate session responses

note right
Headers: `X-Access-Token, X-Access-Token-Expired-At, X-Refresh-Access-Token, X-Refresh-Access-Token-Expired-At`
end note

else Refresh token expired

client -> client: X-Refresh-Access-Token expired

note right
Repeat Authenticate session
end note

end

```

## Entity Relationship Diagram (ERD)


### SQL Database

```plantuml
entity AuthProvider {
    ID: smallint <<PK>>
    --
    slug: varchar(255) <<UNIQUE>>
    metadata: JSON
    createdAt: datetime
    updatedAt: datetime
    modifiedBy: JSON
    version: bigint
}

note left
metadata = {
    saltRounds
    providerURL
    environment
}
end note

entity ClientType {
    ID: smallint <<PK>>
    --
    name: varchar(255)
    createdAt: datetime
    updatedAt: datetime
    modifiedBy: JSON
    version: bigint
}

entity ClientAuth {
    ID: smallint <<PK>>
    --
    typeID: smallint
    clientID: varchar(8)
    credentials: JSON
    metadata: JSON
    createdAt: datetime
    updatedAt: datetime
    modifiedBy: JSON
}

note right
credentails = {
    clientSecret
    clientLifetime
}
end note

entity AdminAuth {
    ID: bigint <<PK, AI>>
    --
    userID: bigint <<FK>>
    username: varchar(255) <<UNIQUE>>
    authProviderID: smallint
    credential: tinytext <<NULLABLE>>
    statusID: smallint
    metadata: JSON
    createdAt: datetime
    updatedAt: datetime
    modifiedBy: JSON
    version: bigint
}

note right
metadata = {
    super: bool
}
end note

ClientAuth "typeID"||--||"ID"ClientType

AdminAuth "authProviderID"||--||"ID" AuthProvider

note "statusID = {\n\tINACTIVE: 1\n\tACTIVE: 2\n}" as status


entity ContentType {
    ID: smallint <<PK>>
    --
    name: varchar(255)
    createdAt: datetime
    updatedAt: datetime
    modifiedBy: JSON
    version: bigint
}
note top
ContentType = {
    LIVE: 1
    AOD: 2
    VOD: 3
}
end note

entity ContentCategory {
    ID: smallint <<PK>>
    --
    slug: varchar(255) <<UNIQUE>>
    name: varchar(255)
    imageURL: varchar(511) <<NULLABLE>>
    statusID: smallint
    metadata: JSON
    createdAt: datetime
    updatedAt: datetime
    modifiedBy: JSON
    version: bigint
}

note top
metadata = {
    description
}

ContentCategory = [slug][int] {
    comedy: 1
    horror: 2
    lifestyle: 4
    stories: 5
    audio-series: 6
    music: 7
    business: 9
    kids-family: 10
    health: 11
    sports: 12
    religion: 13
    audiobook: 14
    religion-islam: 15
    religion-kristen: 16
    religion-buddha = 17
    religion-hindu = 18
    religion-kong-hu-cu = 19
    religion-katolik = 20
    video = 21
}
end note

entity UserAuth {
    ID: bigint <<PK, AI>>
    --
    extID: bigint <<UNIQUE>>
    userID: bigint <<FK>>
    authProviderID: smallint
    credential: tinytext
    statusID: smallint
    metadata: JSON
    createdAt: datetime
    updatedAt: datetime
    modifiedBy: JSON
    version: bigint
}

entity User {
    ID: bigint <<PK, AI>>
    --
    email: varchar(255) <<NULLABLE>>
    fullName: varchar(255)
    avatarURL: varchar(511) <<NULLABLE>>
    metadata: JSON
    createdAt: datetime
    updatedAt: datetime
    modifiedBy: JSON
    version: bigint
}
note top
metadata = {
    gender
    phoneNumber
    DOB
    address
}
end note

entity Content {
    ID: bigint <<PK, AI>>
    --
    extID: varchar(12) <<UNIQUE>>
    slug: varhcar(255) <<UNIQUE>>
    name: varchar(255)
    userID: bigint <<FK>>
    statusID: smallint
    typeID: smallint
    cityID: smallint
    sort: bigint
    metadata: JSON
    highlight: bool
    createdAt: datetime
    updatedAt: datetime
    modifiedBy: JSON
    version: bigint
}

note right
metadata = {
    description
    bannerURL
    thumbnailURL
    extBannerURL
    liveMetadata
}
end note

entity ContentData {
    ID: bigint <<PK, AI>>
    --
    extID: varchar(12) <<UNIQUE>>
    slug: varhcar(255) <<UNIQUE>>
    title: varchar(255)
    contentID: bigint <<FK>>
    statusID: smallint
    dataURL: varchar(511)
    moderationStatusID: smallint
    sort: bigint
    highlight: bool
    metadata: JSON
    createdAt: datetime
    updatedAt: datetime
    modifiedBy: JSON
    version: bigint
}
note right
metadata = {
  description
  bannerURL
  thumbnailURL
}
end note

entity ContentHasCategory {
    ID: bigint <<PK, AI>>
    --
    contentID: bigint <<FK>>
    categoryID: smallint <<FK>>
    createdAt: datetime
}

entity Template {
    ID: bigint <<PK, AI>>
    --
    name: varchar(255)
    typeID: smallint
    statusID: smallint
    dataURL: varhcar(511) <<NULLABLE>>
    metadata: JSON
    createdAt: datetime
    updatedAt: datetime
    modifiedBy: JSON
    version: bigint
}
note right
typeID = {
    BACKGROUND-MUSIC: 1
    COVER: 2
}
end note

entity City {
    ID: smallint <<PK>>
    --
    slug: varhcar(255) <<UNIQUE>>
    name: varchar(255)
    createdAt: datetime
    updatedAt: datetime
    modifiedBy: JSON
    version: bigint
}

User "ID"||--|>"userID" Content
ContentCategory "ID"<|--||"categoryID" ContentHasCategory
Content "ID"<|--||"contentID" ContentHasCategory
ContentType "ID"||--||"typeID"Content

Content "ID"||--|>"contentID"ContentData
City "ID"||--|| "cityID" Content

User "ID" ||--||"userID" UserAuth
User "ID" ||--||"userID" AdminAuth

@enduml

@enduml
```

```plantuml
entity News {
    ID: bigint <<PK, AI>>
    --
    slug: varchar(255)
    title: varchar(255)
    sort: bigint
    categoryID: smallint
    content: text
    statusID: smallint
    metadata: JSON
    createdAt: datetime
    updatedAt: datetime
    modifiedBy: JSON
    version: bigint
}

note left
metadata = {
    bannerURL
    imageURL
    videoURL
    embedURL
}
end note

entity NewsCategory {
    ID: smallint <<PK, AI>>
    --
    slug: varchar(255) <<UNIQUE>>
    name: varchar(255)
    imageURL: varchar(511) <<NULLABLE>>
    statusID: smallint
    createdAt: datetime
    updatedAt: datetime
    modifiedBy: JSON
    version: bigint
}

entity NewsTag {
    ID: smallint <<PK>>
    --
    name: varchar(255)
    statusID: smallint
    createdAt: datetime
    updatedAt: datetime
    modifiedBy: JSON
    version: bigint
}

entity NewsHasTag {
    ID: bigint <<PK, AI>>
    --
    tagID: smallint
    newsID: bigint
    createdAt: datetime
}

entity Campaign {
    ID: bigint <<PK, AI>>
    --
    typeID: smallint
    name: varchar(255)
    metadata: JSON
    statusID: smallint
    sort: bigint
    createdAt: datetime
    updatedAt: datetime
    modifiedBy: JSON
    version: bigint
}

note right
typeID = {
    PROMOTION: 1
    TOP_ARTIST: 2
}

metadata = {
    bannerURL
    content
}
end note

News "categoryID"||--||"ID" NewsCategory
NewsHasTag "newsID"||--|>"ID" News
NewsHasTag "tagID"||--|>"ID" NewsTag
```

### NoSQL

```plantuml
entity Settings {
    ID: Objecttd
    --
    extID: int
    key: string
    description: string
    value: string | []any
    isArray: bool
    createdAt: date
    updatedAt: date
}

entity Privilege {
    ID: ObjectId
    --
    extID: string
    ability: string
    createdAt: date
    updatedAt: date
}

entity SubjectPrivileges {
    ID: ObjectId
    --
    subjectExtID: string
    name: string
    privilegeExtID: string
    createdAt: date
    privilege: Privilege
}
```

## Migrations

```plantuml
entity Template {}

note right
from backsounds:
    name = backsounds.name
    statusID = backsounds.active
    dataURL = backsounds.url
    metadata = {
        duration: backsounds.duration
    }
end note
```

```plantuml
entity UserAuth {}

note right
extID = userdetail.UDSso_id
statusID = UDStatus
metadata = {
    SSOUnique: userdetail.UDSsoUnique
}
end note
```

```plantuml
entity User {}
note right
email = userdetail.UDEmail
fullName = userdetail.UDFirstname+userdetail.UDLastname
avatarURL = userdetail.UDUrl_profile
metadata = {
    phoneNumber: userdetail.UDPhoneNumber
    address: userdetail.UDAddress
    DOB: userdetail.UDDateOfBirth
    gender: userdetail.UDGender
}
end note
```


```plantuml
entity Content {}
note right

podcasts:
    slug = kebabCase(podcasts.title)
    name = podcasts.title
    userID = podcasts.created_by
    typeID = AOD
    statusID = podcasts.status
    metadata = {
        description: podcasts.description
        bannerURL: podcasts.image_banner
        thumbnailURL: podcasts.image_name
    }
    sort = podcasts.sort

radios:
    slug = kebabCase(radios.name)
    name = radios.name
    userID = radios.created_by
    typeID = LIVE
    statusID = radios.status
    metadata = {
        bannerURL: radios.image_banner
        thumbnailURL: radios.image_name
        extBannerURL: radios.image_banner_radio_partner
        liveMetadata: {
            frequency: radios.frequency
            city: radios.city
            address: radios.address
            geoPoint: {
                latitude: radios.latitude
                longitude: radios.longitude
            }
            phoneNumber: radios.phone
            website: radios.website
            fax: radios.fax
            email: radios.email
            socialMedia: {
                whatsapp: radios.whatsapp
                facebook: radios.facebook
                twitter: radios.twitter
                instagram: radios.instagram
            }
        }
    }
    sort = radios.sort_network

videos:
    slug = kebabCase(videos.title)
    name = videos.title
    userID = videos.created_by
    typeID = VIDEO
    statusID = videos.status
    metadata = {
        description: videos.description
        thumbnailURL: videos.image_name
    }
    sort = videos.sort

spirituals:
    slug = kebabCase(spirituals.title)
    name = sprituals.title
    userID = spirituals.created_by
    typeID = AOD
    statusID = spirituals.status
    metadata = {
        description: spirituals.description
        thumbnailURL: spirituals.image_name
        bannerURL: spirituals.image_banner
    }
    sort = spirituals.sort
end note
```

```plantuml
entity ContentHasCategory {}
note right
podcasts:
    categoryID = podcasts.categoryPodcasts_id

sprituals:
    categoryID = spirituals.category_id

radios:
    categoryID = Music for radios.statusRadios == 'rMusic'
end note
```


```plantuml
entity ContentData {}
note right
content_podcasts:
    slug = kebabCase(content_podcasts.title)
    title = content_podcasts.title
    contentID = content_podcasts.podcasts_id
    statusID = content_podcasts.status
    dataURL = content_podcasts.embed_url OR content_podcasts.filename
    moderationStatusID = content_podcasts.moderation_status
    metadata = {
        description: content_podcasts.description
        thumbnailURL: content_podcasts.image_name
        bannerURL: content_podcasts.image_url
    }
    sort: content_podcasts.sort

content_spirituals:
    slug = kebabCase(content_spirituals.title)
    title = content_spirituals.title
    contentID = spritiuals_having_content.content_id == content_spirituals.id
    statusID = content_spirituals.status
    dataURL = content_spirituals.embed_url OR content_spirituals.filename
    moderationStatusID = APPROVE
    metadata = {
        thumbnailURL: content_spirituals.image_name
        bannerURL: content_spirituals.image_banner
    }

radios:
    slug = kebabCase(radios.name)
    title = radios.name
    contentID = Content.slug == kebabCase(radios.name)
    statusID = radios.status
    dataURL = radios.audio_url
    moderationStatusID = APPROVE
    highlight = radios.statusRadios == 'rBanner'
    metadata = {
        bannerURL: radios.image_banner
        thumbnailURL: radios.image_name
    }
    sort: radios.sort_banner || radios.sort_music

videos:
    slug = kebabCase(videos.title)
    title = videos.title
    contentID = Content.slug == kebabCase(videos.title)
    statusID = videos.status
    dataURL = videos.embed_url OR videos.filename
    moderationStatusID = APPROVE
    metadata = {
        thumbnailURL: videos.image_name
    }
    sort: videos.sort
end note
```

```plantuml
entity News {}
    
note right
news:
    slug = kebabCase(news.title)
    title = news.title
    sort = news.sort
    category_id = news.category_id
    content = news.content
    metadata = {
        bannerURL: news.image_banner
        imageURL: news.image_content
        videoURL: news.videos_content
        embedURL: news.embed_url
    }
end note
```

```plantuml
entity NewsHasTag {}

note right
news_having_tag:
    news_id: news_id == news.id
    tag_id: tag_id == news_tag.id
end note
```

```plantuml
entity Campaign {}

note right
content_topartists:
    typeID = TOP_ARTIST
    name = title
    sort = sort
    metadata = {
        bannerURL: image_banner
        content: content
    }
    statusID = status
end note
```

### Content/Content Data ExtID

```json=
CONTENT_extID_PREFIX = {
    PODCAST: 'P-',
    RADIO: 'R-',
    SPIRITUAL: 'S-',
    VIDEO: 'V-',
    NEWS: 'N-',
}
```


## Development

### Credentials
```json
  > CMS App Client credential. ClientId = DIZD9JPG ClientSecret = oatfUPrKt3onkif28103c6DnHGTkyOIo
  > Web App Client credential. ClientId = ODOLLDGT ClientSecret = YSwDkSK1XikqIFPihyfvj8t9QmmNC8hC
  > Mobile App Client credential. ClientId = G1UP424T ClientSecret = ptouROa31JE81sacnRZo5x8a2oF5VHqU
  > H2H Client credential. ClientId = CEP3UDOL ClientSecret = aezeen7chunievoh6ar1mohk0Lae6pah

  > Super Administrator credential. Username = superadmin Password = 9djqf2MWl29j1jvMr5tHWDfYS4TCYcTb
```

### Mobile Integration

#### Home Curation

```json=
VIEW_TYPE = {
    UNKNOWN: 0,
    SQUARE_GRID: 1,
    HORIZONTAL_LIST: 2,
    TITLE: 3,
    CAROUSEL: 4,
    MINI_PLAYER: 5,
    VERTICAL_LIST: 6,
    PORTRAIT_GRID: 7,
    LANDSCAPE_GRID: 8,
    COMMENT_LIST: 9,
    BANNER: 10,
    SPECIAL_BANNER: 11,
    COMMENT_FORM: 12,
    LANDSCAPE_TILE: 13
}

DATA_TYPE = {
    OBJECT: 1,
    ARRAY: 2,
    STRING: 3,
    INTEGER: 4,
    DECIMAL: 5,
    BOOLEAN: 6,
    ARRAY_CURRATOR_OBJECT: 7,
    ARRAY_STRING: 8,
    ARRAY_INTEGER: 9,
    ARRAY_DECIMAL: 10,
    ARRAY_BOOLEAN: 11,
}
```

#### API Response Body

```json=
BODY = {
    data: Object
    meta: Object
    status: Object
}
```

##### CURRATOR OBJECT

```json=
CURRATOR_OBJECT = {
        viewTypeID,
        dataTypeID,
        data,
        callback: {
            url,
            method,
            query,
        },
}
```

#### Mobile response convention

**These responses is mapped based on VIEW_TYPE**

##### LANDSCAPE | PORTRAIT | SQUARE_GRID

```json=
{
    "ID": number,
    "title": string,
    "subtitle": string,
    "imageURL": string
}
```

###### PORTRAIT GRID

![](https://i.imgur.com/89tVU1y.jpg)

###### LANDSCAPE_GRID

![](https://i.imgur.com/P8TWSVE.png)

###### SQURE_GRID

![](https://i.imgur.com/lO4K7BB.png)

##### SPECIAL_BANNER
```json=
{
    "imageURL": string,
    "title": string,
}
```

![](https://i.imgur.com/KfHQgWF.jpg)

##### CAROUSEL

```json=
{
    "ID": number,
    "title": string,
    "subtitle": string
}
```

![](https://i.imgur.com/smcOPPW.png)


##### MINI_PLAYER

```json=
{
    "ID": number,
    "dataURL": string,
    "title": string,
    "subtitle": string
}
```

![](https://i.imgur.com/txP5Liw.png)


##### BANNER | TITLE

`string` on field `data`

###### BANNER
![](https://i.imgur.com/rkRtTM9.jpg)

###### TITLE

![](https://i.imgur.com/Z2bvkh7.png)


##### COMMENT_LIST

```json=
{
    "ID": number,
    "createdAt": number,
    "likes": number,
    "replies": [
        "ID": number,
        "createdAt": number,
        "user": {
            "ID": number,
            "fullName": string,
            "avatarURL": string
        },
        "message": string
    ],
    "user": {
        "ID": number,
        "fullName": string,
        "avatarURL": string
    },
    "message": string
}
```

![](https://i.imgur.com/QH1d3xt.png)


##### VERTICAL_LIST

```json=
{
    "ID": number,
    "imageURL": string,
    "title": string,
    "subtitle": string,
    "intro": string
}
```

![](https://i.imgur.com/pwNDsQA.jpg)


##### COMMENT_FORM

![](https://i.imgur.com/PY1f126.png)

##### LANDSCAPE_TILE

```json=
{
    "ID": number,
    "title": string,
    "imageURL": string
}
```

![](https://i.imgur.com/e1pgaJ1.png)
