11 | ) {
12 | res.status(200).json({ name: 'John Doe' })
13 | }
14 |
--------------------------------------------------------------------------------
/CustomSearchPage/pages/components/landingPage.tsx:
--------------------------------------------------------------------------------
1 | import { NextPage } from "next";
2 | import styles from "../../styles/Startup.module.css"
3 | import Image from 'next/image'
4 | import banner from '../../assets/header.png'
5 |
6 |
7 |
8 | const LandingPage: NextPage = () => {
9 | return (
10 |
11 |
18 |
19 |
22 |
23 |
28 |
29 | )
30 | }
31 |
32 | export default LandingPage;
33 |
--------------------------------------------------------------------------------
/CustomSearchPage/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import type { NextPage } from 'next'
2 | import Head from 'next/head'
3 | import Image from 'next/image'
4 | import styles from '../styles/Home.module.css'
5 | import LandingPage from "./components/landingPage";
6 |
7 | const Home: NextPage = () => {
8 | return (
9 |
10 |
11 |
Yuuriya Search!
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | )
20 | }
21 |
22 | export default Home
23 |
--------------------------------------------------------------------------------
/CustomSearchPage/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FirmanKurniawan/NodeJS-Projects/f520cdd8e4e1163e3414ba5a6692b24ecad95462/CustomSearchPage/public/favicon.ico
--------------------------------------------------------------------------------
/CustomSearchPage/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/CustomSearchPage/styles/Home.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | padding: 0 2rem;
3 | }
4 |
5 | .main {
6 | min-height: 100vh;
7 | padding: 4rem 0;
8 | flex: 1;
9 | display: flex;
10 | flex-direction: column;
11 | justify-content: center;
12 | align-items: center;
13 | }
14 |
15 | .footer {
16 | display: flex;
17 | flex: 1;
18 | padding: 2rem 0;
19 | border-top: 1px solid #eaeaea;
20 | justify-content: center;
21 | align-items: center;
22 | }
23 |
24 | .footer a {
25 | display: flex;
26 | justify-content: center;
27 | align-items: center;
28 | flex-grow: 1;
29 | }
30 |
31 | .title a {
32 | color: #0070f3;
33 | text-decoration: none;
34 | }
35 |
36 | .title a:hover,
37 | .title a:focus,
38 | .title a:active {
39 | text-decoration: underline;
40 | }
41 |
42 | .title {
43 | margin: 0;
44 | line-height: 1.15;
45 | font-size: 4rem;
46 | }
47 |
48 | .title,
49 | .description {
50 | text-align: center;
51 | }
52 |
53 | .description {
54 | margin: 4rem 0;
55 | line-height: 1.5;
56 | font-size: 1.5rem;
57 | }
58 |
59 | .code {
60 | background: #fafafa;
61 | border-radius: 5px;
62 | padding: 0.75rem;
63 | font-size: 1.1rem;
64 | font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
65 | Bitstream Vera Sans Mono, Courier New, monospace;
66 | }
67 |
68 | .grid {
69 | display: flex;
70 | align-items: center;
71 | justify-content: center;
72 | flex-wrap: wrap;
73 | max-width: 800px;
74 | }
75 |
76 | .card {
77 | margin: 1rem;
78 | padding: 1.5rem;
79 | text-align: left;
80 | color: inherit;
81 | text-decoration: none;
82 | border: 1px solid #eaeaea;
83 | border-radius: 10px;
84 | transition: color 0.15s ease, border-color 0.15s ease;
85 | max-width: 300px;
86 | }
87 |
88 | .card:hover,
89 | .card:focus,
90 | .card:active {
91 | color: #0070f3;
92 | border-color: #0070f3;
93 | }
94 |
95 | .card h2 {
96 | margin: 0 0 1rem 0;
97 | font-size: 1.5rem;
98 | }
99 |
100 | .card p {
101 | margin: 0;
102 | font-size: 1.25rem;
103 | line-height: 1.5;
104 | }
105 |
106 | .logo {
107 | height: 1em;
108 | margin-left: 0.5rem;
109 | }
110 |
111 | @media (max-width: 600px) {
112 | .grid {
113 | width: 100%;
114 | flex-direction: column;
115 | }
116 | }
117 |
118 | @media (prefers-color-scheme: dark) {
119 | .card,
120 | .footer {
121 | border-color: #222;
122 | }
123 | .code {
124 | background: #111;
125 | }
126 | .logo img {
127 | filter: invert(1);
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/CustomSearchPage/styles/Startup.module.css:
--------------------------------------------------------------------------------
1 | .root{
2 | background-color: white;
3 | min-height: 100vh;
4 | }
5 |
6 | .header {
7 | min-height: 60rempx;
8 | display: flex;
9 | justify-content: center;
10 | padding: 10rem 0 0 0;
11 | }
12 |
13 |
14 |
15 | .banner {
16 | object-fit: contain;
17 | object-position: center bottom;
18 | }
19 |
20 | .ascii {
21 | font-family: monospace;
22 | white-space: pre;
23 | display: flex;
24 | justify-content: center;
25 | width: 50%;
26 | }
27 |
28 | .searchBox {
29 | display: flex;
30 | justify-content: center;
31 | padding: 3rem 0 0 0;
32 | }
33 | .searchBar {
34 | border-radius: 24px;
35 | width: auto;
36 | max-width: 584px;
37 | border: 1px solid #5f6368;
38 | box-shadow: none;
39 | z-index: 3;
40 | margin: 0 auto;
41 | min-width: 40rem;
42 | min-height: 2rem;
43 | border-radius: 30px;
44 | background-color: #fff;
45 | color: #000;
46 | font-size: 30px;
47 | text-align: center;
48 |
49 | }
50 |
51 | .searchBar::placeholder {
52 | text-align: center;
53 | }
54 |
55 |
56 | .info {
57 | display: flex;
58 | justify-content: center;
59 | color: black;
60 | padding-inline-start: 0;
61 | list-style: none;
62 | list-style-position: outside;
63 | gap: 3rem;
64 | padding: 1.5rem;
65 | }
66 |
67 |
68 | @media (max-width: 600px) {
69 | .searchBar {
70 | min-width: 17rem;
71 | font-size: 15px;
72 | }
73 | .header {
74 | min-height: 60rempx;
75 | display: flex;
76 | justify-content: center;
77 | padding: 15rem 0 0 0;
78 | }
79 | .ascii {
80 | font-family: monospace;
81 | white-space: pre;
82 | display: flex;
83 | justify-content: center;
84 | width: 75%;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/CustomSearchPage/styles/globals.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | padding: 0;
4 | margin: 0;
5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
6 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
7 | }
8 |
9 | a {
10 | color: inherit;
11 | text-decoration: none;
12 | }
13 |
14 | * {
15 | box-sizing: border-box;
16 | }
17 |
18 | @media (prefers-color-scheme: dark) {
19 | html {
20 | color-scheme: dark;
21 | }
22 | body {
23 | color: white;
24 | background: black;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/CustomSearchPage/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true
17 | },
18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NodeJS-Projects
2 | # hacktoberfest
3 |
4 | ## Contributing
5 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
6 |
7 | Please make sure to update tests as appropriate.
8 |
9 | ## License
10 | [MIT](https://choosealicense.com/licenses/mit/)
11 |
12 | hacktoberfest 2022
13 | hacktoberfest-indonesia
14 | GLOBAL CONTRIBUTION
15 |
--------------------------------------------------------------------------------
/Travelling_Salesman.js:
--------------------------------------------------------------------------------
1 | // A salesman has a number of cities to visit. They want to calculate the total number of possible paths they could take, visiting each city once before returning home. Return the total number of possible paths a salesman can travel, given n cities.
2 |
3 | // this uses a factorial to solve it.
4 |
5 | function paths(n) {
6 | if (n < 0) return;
7 | if (n === 0) return 1;
8 | return n * paths(n - 1);
9 | }
10 |
11 | console.log(paths(4)) // 24
12 | console.log(paths(1)) // 1
13 | console.log(paths(9)) // 362880
14 |
--------------------------------------------------------------------------------
/basic-challange/PaswordPattern.js:
--------------------------------------------------------------------------------
1 | const pattern = [
2 | [2, 4, 5],
3 | [1, 3, 4, 5, 6],
4 | [2, 4, 5, 6],
5 | [1, 2, 5, 7, 8],
6 | [1, 2, 3, 4, 6, 7, 8, 9],
7 | [2, 3, 5, 8, 9],
8 | [4, 5, 8],
9 | [4, 5, 6, 7, 9],
10 | [5, 6, 8]
11 | ]
12 |
13 | const validate = (num) => {
14 | if (num.length > 7) {
15 | return false
16 | }
17 |
18 | if (num.length < 5) {
19 | return false
20 | }
21 |
22 | return true
23 | }
24 |
25 | const comparePattern = (a, b) => {
26 | let state = false
27 | if (pattern[a-1].includes(parseInt(b))) {
28 | state = true
29 | }else{
30 | state = false
31 | }
32 | return state
33 | }
34 |
35 | const solution = (input) => {
36 |
37 | console.log("Input : ", input)
38 | let state = false
39 |
40 | if (validate(input)) {
41 | let split = input.split("")
42 | let t = 1
43 |
44 | for (let i = 0; i < split.length; i++) {
45 | if (split.length > t) {
46 | state = comparePattern(split[i], split[t])
47 | t++
48 | }
49 | }
50 | }
51 |
52 | return state ? "#YA" : "#TIDAK"
53 | }
54 |
55 | console.log(solution("12321"))
56 | console.log(solution("23654789"))
57 | console.log(solution("512369"))
--------------------------------------------------------------------------------
/basic-challange/ShuffleArray.ts:
--------------------------------------------------------------------------------
1 | export function shuffleArray(array: Array): Array {
2 | let currentIndex = array.length,
3 | randomIndex;
4 | // While there remain elements to shuffle...
5 | while (0 !== currentIndex) {
6 | // Pick a remaining element...
7 | randomIndex = Math.floor(Math.random() * currentIndex);
8 | currentIndex--;
9 | // And swap it with the current element.
10 | [array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]];
11 | }
12 | return array;
13 | }
14 |
15 | // Consume utility
16 | interface People {
17 | name: string;
18 | gender: "M" | "F";
19 | }
20 |
21 | const peoples = [{name: "Pangeran", gender: "M"}, {name: "Raja", gender: "M"}, {name: "Ratu", gender: "F"}];
22 |
23 | // The result will shuffle the array
24 | console.log(shuffleArray(peoples))
25 |
--------------------------------------------------------------------------------
/basic-challange/Triangle.js:
--------------------------------------------------------------------------------
1 | const triangle = (n) => {
2 | let x = []
3 | let y = []
4 |
5 | for(let j = n; j > 0; j--){
6 | for(let i = 0; i < j; i++){
7 | x.push('*')
8 | }
9 | console.log(x.join(''))
10 | x = []
11 | }
12 |
13 | for(let j = 1; j < n; j++){
14 | for(let i = -1; i < j; i++){
15 | y.push('*')
16 | }
17 | console.log(y.join(''))
18 | y = []
19 | }
20 | }
21 |
22 | triangle(10)
--------------------------------------------------------------------------------
/basic-challange/bst.js:
--------------------------------------------------------------------------------
1 | // class node merupakan isi dari suatu node yang akan dibangun
2 | // parameter yang dimiliki suatu node antara lain:
3 | // ada value atau data, ada child kiri dan child kanan
4 | class Node {
5 | constructor(value,left=null,right=null) {
6 | this.value = value
7 | this.left = left
8 | this.right = right
9 | }
10 | }
11 |
12 | class BST {
13 | constructor() {
14 | this.root = null;
15 | }
16 |
17 | insert(data) {
18 | let newNode = new Node(data)
19 |
20 | this.root == null ? this.root = newNode : this.insertNode(this.root, newNode)
21 | }
22 |
23 | insertNode(node, newNode){
24 | if(newNode.data < node.data) {
25 | if(node.left === null) {
26 | node.left = newNode
27 | } else {
28 | this.insertNode(node.left, newNode)
29 | }
30 | } else {
31 | if(node.right === null) {
32 | node.right = newNode
33 | } else {
34 | this.insertNode(node.right, newNode)
35 | }
36 | }
37 | }
38 | }
39 |
40 | // rereference
41 | // https://www.geeksforgeeks.org/implementation-binary-search-tree-javascript/
--------------------------------------------------------------------------------
/basic-challange/complementaryDna.js:
--------------------------------------------------------------------------------
1 | // Deoxyribonucleic acid (DNA) is a chemical found in the nucleus of cells and carries the "instructions" for the development and functioning of living organisms.
2 |
3 | // If you want to know more: http://en.wikipedia.org/wiki/DNA
4 |
5 | // In DNA strings, symbols "A" and "T" are complements of each other, as "C" and "G". Your function receives one side of the DNA (string, except for Haskell); you need to return the other complementary side. DNA strand is never empty or there is no DNA at all (again, except for Haskell).
6 |
7 | const convert = (dna) => {
8 | let newArr = []
9 | let tmp = dna.split('').map(e => {
10 | if(e == 'A') {
11 | newArr.push('T')
12 | }
13 | if(e == 'T') {
14 | newArr.push('A')
15 | }
16 | if(e == 'C') {
17 | newArr.push('G')
18 | }
19 | if(e == 'G') {
20 | newArr.push('C')
21 | }
22 | })
23 |
24 | return newArr.join('')
25 | }
26 | let str = 'AAAC'
27 | let str = 'ATTGC'
28 | let str = 'GTAT'
29 |
30 | console.log(convert(str))
--------------------------------------------------------------------------------
/basic-challange/convertToBinary.js:
--------------------------------------------------------------------------------
1 | // Given a non-negative integer n,
2 | // write a function to_binary/ToBinary which returns that number in a binary format.
3 |
4 | const to_binary = (n) => {
5 | // easiest way convert to binary
6 | // return +n.toString(2)
7 | let bin = ""
8 | if(n < 0) return 'Must positive integer n'
9 | if(n === 1) return 1
10 | while (n > 0) {
11 | if( n % 2 == 1) {
12 | bin = "1" + bin
13 | } else {
14 | bin = "0" + bin
15 | }
16 | n = Math.floor(n / 2)
17 | }
18 | return bin
19 | }
20 |
21 | let dec = 10
22 | console.log(to_binary(dec))
23 |
24 | // step by step
25 | // 1. jika n == 1 maka akan mengembalikan nilai tersebut
26 | // 2. jika n angka negatif maka akan dikembalikan string "harus bernilai integer positif"
27 | // 3. perulangan secara terus menerus selagi n lebih dari 0
28 | // 4. jika nilai n di modulo 2 == 1 maka akan memasukkan binary bernilai 1 pada varible bin
29 | // 5. jika !== 1 maka akan memasukkan nilai berupa 0 ke variable bin
30 | // 6. nilai n akan dibagi 2 dan dibulatkan ke nilai integer floor agar
--------------------------------------------------------------------------------
/basic-challange/createaeslaknat.js:
--------------------------------------------------------------------------------
1 | var message = document.getElementById('msg').value;
2 | var key= '123456';
3 | var iv = '1234567891011'
4 |
5 | function enkrip(){
6 |
7 | var encrypted = CryptoJS.AES.encrypt(message, key, { iv: iv, mode: CryptoJS.mode.CBC, });
8 | // var encrypted = CryptoJS.AES.encrypt(message, key);
9 | alert(encrypted.toString());
10 | console.log(encrypted.toString());
11 | console.log(encrypted);
12 | }
13 |
14 | function dekrip(){
15 | var decrypted = CryptoJS.AES.decrypt(message, key, { iv: iv, mode: CryptoJS.mode.CBC, });
16 | alert(decrypted.toString(CryptoJS.enc.Utf8))
17 | console.log(decrypted.toString(CryptoJS.enc.Utf8));
18 | }
19 |
20 | const password = 'secure secret key'
21 |
22 | const encrypt = (content, password) => CryptoJS.AES.encrypt(JSON.stringify({ content }), password).toString()
23 | const decrypt = (crypted, password) => JSON.parse(CryptoJS.AES.decrypt(crypted, password).toString(CryptoJS.enc.Utf8)).content
24 |
25 | // Encrypt
26 | const encryptedString = encrypt('This is a string', password)
27 | const encryptedObject = encrypt({ test: 'This is an object' }, password)
28 | console.log(encryptedString)
29 | console.log(encryptedObject)
30 |
31 | // Decrypt
32 | const decryptedString = decrypt(encryptedString, password)
33 | const decryptedObject = decrypt(encryptedObject, password)
34 | console.log(decryptedString)
35 | console.log(decryptedObject)
36 |
--------------------------------------------------------------------------------
/basic-challange/deepSum.js:
--------------------------------------------------------------------------------
1 | function deepSum(arr) {
2 | let result = 0;
3 | arr.forEach((element) => {
4 | if (typeof element == "object") {
5 | result += deepSum(element);
6 | } else {
7 | result += element;
8 | }
9 | });
10 | return result;
11 | }
12 |
13 | console.log(
14 | deepSum([
15 | [
16 | [4, 5, 6],
17 | [9, 1, 2, 10],
18 | [9, 4, 3],
19 | ],
20 | [
21 | [4, 14, 31],
22 | [9, 10, 18, 12, 20],
23 | [1, 4, 90],
24 | ],
25 | [
26 | [2, 5, 10],
27 | [3, 4, 5],
28 | [2, 4, 5, 10],
29 | ],
30 | ])
31 | ); // 316
32 |
33 | console.log(
34 | deepSum([
35 | [[20, 10], [15], [1, 1]],
36 | [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], [2], [9, 11]],
37 | [[3, 5, 1], [1, 5, 3], [1]],
38 | [[2]],
39 | ])
40 | ); // 156
41 |
42 | console.log(deepSum([])); // No number
43 |
44 | console.log(
45 | deepSum([
46 | [[20, 10], [15], [1, 1]],
47 | [[1, 2, 3, 9, 10, 11], [2], [9, 11]],
48 | [[2]],
49 | ])
50 | );
51 |
--------------------------------------------------------------------------------
/basic-challange/flippingMatrix.js:
--------------------------------------------------------------------------------
1 | function flippingMatrix(array) {
2 | let temp = {
3 | sum: null,
4 | index: null,
5 | };
6 |
7 | array.forEach((element, index) => {
8 | let count = 0;
9 |
10 | element.forEach((element) => {
11 | count += element;
12 | });
13 |
14 | if (temp.sum == null || temp.sum < count) {
15 | (temp.sum = count), (temp.index = index);
16 | }
17 | });
18 |
19 | array.forEach((element, index) => {
20 | if (index === temp.index) {
21 | element.reverse();
22 | }
23 | });
24 | return array;
25 | }
26 |
27 | console.log(
28 | flippingMatrix([
29 | [1, 2],
30 | [3, 4],
31 | ])
32 | );
33 |
34 | console.log(
35 | flippingMatrix([
36 | [3, 0, 9],
37 | [10, 7, 3],
38 | [9, 4, 1],
39 | ])
40 | );
41 |
--------------------------------------------------------------------------------
/basic-challange/mostFrequentLargestNumbers.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Implementasikan function sorting dan getTotal untuk mendapatkan angka yang paling besar dan mengetahui
3 | * berapa kali angka tersebut muncul di dalam arrNumber.
4 | * Dilarang mengubah mengubah 2 lines di dalam function mostFrequentLargestNumbers yaitu lines:
5 | * let listSort = sorting(arrNumber)
6 | * let countHighest = getTotal(listSort)
7 | */
8 | function sorting(arrNumber) {
9 | return arrNumber.sort((a, b) => b - a);
10 | }
11 |
12 | function getTotal(arrNumber) {
13 | if (arrNumber.length === 0) {
14 | return "";
15 | }
16 |
17 | let count = 0;
18 | let largestNumber = arrNumber[0];
19 |
20 | arrNumber.forEach((element) => {
21 | if (element === largestNumber) {
22 | count += 1;
23 | }
24 | });
25 |
26 | return `angka paling besar adalah ${largestNumber} dan jumlah kemunculan sebanyak ${count} kali`;
27 | }
28 |
29 | function mostFrequentLargestNumbers(arrNumber) {
30 | let listSort = sorting(arrNumber);
31 | let countHighest = getTotal(listSort);
32 | return countHighest;
33 | }
34 |
35 | console.log(mostFrequentLargestNumbers([2, 8, 4, 6, 8, 5, 8, 4]));
36 | //'angka paling besar adalah 8 dan jumlah kemunculan sebanyak 3 kali'
37 |
38 | console.log(
39 | mostFrequentLargestNumbers([122, 122, 130, 100, 135, 100, 135, 150])
40 | );
41 | //'angka paling besar adalah 150 dan jumlah kemunculan sebanyak 1 kali'
42 |
43 | console.log(mostFrequentLargestNumbers([1, 1, 1, 1]));
44 | //'angka paling besar adalah 1 dan jumlah kemunculan sebanyak 4 kali'
45 |
46 | console.log(mostFrequentLargestNumbers([]));
47 | //''
48 |
--------------------------------------------------------------------------------
/blabla.js:
--------------------------------------------------------------------------------
1 | const pattern = [
2 | [2, 4, 5],
3 | [1, 3, 4, 5, 6],
4 | [2, 4, 5, 6],
5 | [1, 2, 5, 7, 8],
6 | [1, 2, 3, 4, 6, 7, 8, 9],
7 | [2, 3, 5, 8, 9],
8 | [4, 5, 8],
9 | [4, 5, 6, 7, 9],
10 | [5, 6, 8]
11 | ]
12 |
13 | const validate = (num) => {
14 | if (num.length > 7) {
15 | return false
16 | }
17 |
18 | if (num.length < 5) {
19 | return false
20 | }
21 |
22 | return true
23 | }
24 |
25 | const comparePattern = (a, b) => {
26 | let state = false
27 | if (pattern[a-1].includes(parseInt(b))) {
28 | state = true
29 | }else{
30 | state = false
31 | }
32 | return state
33 | }
34 |
35 | const solution = (input) => {
36 |
37 | console.log("Input : ", input)
38 | let state = false
39 |
40 | if (validate(input)) {
41 | let split = input.split("")
42 | let t = 1
43 |
44 | for (let i = 0; i < split.length; i++) {
45 | if (split.length > t) {
46 | state = comparePattern(split[i], split[t])
47 | t++
48 | }
49 | }
50 | }
51 |
52 | return state ? "#YA" : "#TIDAK"
53 | }
54 |
55 | console.log(solution("12321"))
56 | console.log(solution("23654789"))
57 | console.log(solution("512369"))
58 |
--------------------------------------------------------------------------------
/bookshelf-api/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "commonjs": true,
4 | "es2021": true,
5 | "node": true
6 | },
7 | "extends": [
8 | "airbnb-base"
9 | ],
10 | "parserOptions": {
11 | "ecmaVersion": 12
12 | },
13 | "rules": {
14 | "no-console": "off"
15 | }
16 | }
--------------------------------------------------------------------------------
/bookshelf-api/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bookshelf-api",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "nodemon ./src/server.js",
8 | "lint": "eslint ./"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "eslint": "^7.29.0",
15 | "eslint-config-airbnb-base": "^14.2.1",
16 | "eslint-plugin-import": "^2.23.4",
17 | "nodemon": "^2.0.7"
18 | },
19 | "dependencies": {
20 | "@hapi/hapi": "^20.1.4",
21 | "nanoid": "^3.1.23"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/bookshelf-api/src/books.js:
--------------------------------------------------------------------------------
1 | const books = [];
2 |
3 | module.exports = books;
--------------------------------------------------------------------------------
/bookshelf-api/src/handler.js:
--------------------------------------------------------------------------------
1 |
2 | const { nanoid } = require('nanoid');
3 | const books = require('./books');
4 |
5 | const addBookHandler = (request, h) => {
6 | const { name, year, author, summary, publisher, pageCount, readPage, reading } = request.payload;
7 |
8 | if ( name === undefined ) {
9 | const response = h.response({
10 | status: 'fail',
11 | message: 'Gagal menambahkan buku. Mohon isi nama buku',
12 | });
13 | response.code(400);
14 | return response;
15 | }
16 |
17 | if (readPage > pageCount ) {
18 | const response = h.response({
19 | status: 'fail',
20 | message: 'Gagal menambahkan buku. readPage tidak boleh lebih besar dari pageCount',
21 | });
22 | response.code(400);
23 | return response;
24 | }
25 |
26 | const id = nanoid(16);
27 |
28 | const insertedAt = new Date().toISOString();
29 | const updatedAt = insertedAt;
30 |
31 | const finished = (pageCount === readPage) ? true : false;
32 |
33 | const newBook = {
34 | id, name, year, author, summary, publisher, pageCount, readPage, finished, reading, insertedAt, updatedAt
35 | };
36 |
37 | books.push(newBook);
38 |
39 | const isSuccess = books.filter((book) => book.id === id).length > 0;
40 |
41 | if (isSuccess) {
42 | const response = h.response({
43 | status: 'success',
44 | message: 'Buku berhasil ditambahkan',
45 | data: {
46 | bookId: id,
47 | },
48 | });
49 | response.code(201);
50 | return response;
51 | }
52 |
53 | const response = h.response({
54 | status: 'fail',
55 | message: 'Catatan gagal ditambahkan',
56 | });
57 | response.code(500);
58 | return response;
59 | };
60 |
61 | const getAllBooksHandler = (request, h) => {
62 | const { name, reading, finished } = request.query;
63 | let listBooks = books;
64 |
65 | if (name !== undefined) {
66 | listBooks = listBooks.filter((book) =>
67 | book.name.toLowerCase().includes(name.toLowerCase())
68 | );
69 | }
70 |
71 | if (reading !== undefined) {
72 | listBooks = listBooks.filter((book) => book.reading == reading);
73 | }
74 |
75 | if (finished !== undefined) {
76 | listBooks = listBooks.filter(
77 | (book) => book.finished == finished
78 | );
79 | }
80 |
81 |
82 |
83 | listBooks = listBooks.map((book) => ({
84 | id: book.id,
85 | name: book.name,
86 | publisher: book.publisher,
87 | }));
88 |
89 | const response = h.response({
90 | status: "success",
91 | data: {
92 | books: listBooks,
93 | },
94 | });
95 |
96 | response.code(200);
97 | return response;
98 | };
99 |
100 |
101 | const getBookByIdHandler = (request, h) => {
102 | const { bookId } = request.params;
103 |
104 | const book = books.filter((book) => book.id === bookId)[0];
105 |
106 | if (book !== undefined) {
107 | return {
108 | status: 'success',
109 | data: {
110 | book,
111 | },
112 | };
113 | }
114 | const response = h.response({
115 | status: 'fail',
116 | message: 'Buku tidak ditemukan',
117 | });
118 | response.code(404);
119 | return response;
120 | };
121 |
122 | const editBookByIdHandler = (request, h) => {
123 | const { bookId } = request.params;
124 |
125 | const { name, year, author, summary, publisher, pageCount, readPage, reading } = request.payload;
126 | const updatedAt = new Date().toISOString();
127 |
128 | if ( name === undefined ) {
129 | const response = h.response({
130 | status: 'fail',
131 | message: 'Gagal memperbarui buku. Mohon isi nama buku',
132 | });
133 | response.code(400);
134 | return response;
135 | }
136 |
137 | if (readPage > pageCount ) {
138 | const response = h.response({
139 | status: 'fail',
140 | message: 'Gagal memperbarui buku. readPage tidak boleh lebih besar dari pageCount',
141 | });
142 | response.code(400);
143 | return response;
144 | }
145 |
146 | const index = books.findIndex((book) => book.id === bookId);
147 |
148 |
149 | if (index !== -1) {
150 | books[index] = {
151 | ...books[index],
152 | name,
153 | year,
154 | author,
155 | summary,
156 | publisher,
157 | pageCount,
158 | readPage,
159 | reading,
160 | };
161 | const response = h.response({
162 | status: 'success',
163 | message: 'Buku berhasil diperbarui',
164 | });
165 | response.code(200);
166 | return response;
167 | }
168 | const response = h.response({
169 | status: 'fail',
170 | message: 'Gagal memperbarui buku. Id tidak ditemukan',
171 | });
172 | response.code(404);
173 | return response;
174 | };
175 |
176 | const deleteBookByIdHandler = (request, h) => {
177 | const { bookId } = request.params;
178 |
179 | const index = books.findIndex((book) => book.id === bookId);
180 |
181 | if (index !== -1) {
182 | books.splice(index, 1);
183 | const response = h.response({
184 | status: 'success',
185 | message: 'Buku berhasil dihapus',
186 | });
187 | response.code(200);
188 | return response;
189 | }
190 |
191 | const response = h.response({
192 | status: 'fail',
193 | message: 'Buku gagal dihapus. Id tidak ditemukan',
194 | });
195 | response.code(404);
196 | return response;
197 | };
198 |
199 | module.exports = { addBookHandler, getAllBooksHandler, getBookByIdHandler, editBookByIdHandler, deleteBookByIdHandler};
200 |
201 | /*
202 | {
203 | "name": "IZ*ONE HISTORY",
204 | "year": 2021,
205 | "author": "CJ ENT",
206 | "summary": "IZONE",
207 | "publisher": "CJ ENT",
208 | "pageCount": "100",
209 | "readPage": "10",
210 | "reading": true
211 | }
212 | */
--------------------------------------------------------------------------------
/bookshelf-api/src/routes.js:
--------------------------------------------------------------------------------
1 | const { addBookHandler, getAllBooksHandler, getBookByIdHandler, editBookByIdHandler, deleteBookByIdHandler } = require('./handler');
2 |
3 | const routes = [
4 | {
5 | method: 'POST',
6 | path: '/books',
7 | handler: addBookHandler,
8 | },
9 | {
10 | method: 'GET',
11 | path: '/books',
12 | handler: getAllBooksHandler,
13 | },
14 | {
15 | method: 'GET',
16 | path: '/books/{bookId}',
17 | handler: getBookByIdHandler,
18 | },
19 | {
20 | method: 'PUT',
21 | path: '/books/{bookId}',
22 | handler: editBookByIdHandler,
23 | },
24 | {
25 | method: 'DELETE',
26 | path: '/books/{bookId}',
27 | handler: deleteBookByIdHandler,
28 | },
29 | ];
30 |
31 | module.exports = routes;
--------------------------------------------------------------------------------
/bookshelf-api/src/server.js:
--------------------------------------------------------------------------------
1 | const Hapi = require('@hapi/hapi');
2 | const routes = require('./routes');
3 |
4 | const init = async () => {
5 | const server = Hapi.server({
6 | port: 5000,
7 | host: 'localhost',
8 | routes: {
9 | cors: {
10 | origin: ['*'],
11 | },
12 | },
13 | });
14 |
15 | server.route(routes);
16 |
17 | await server.start();
18 | console.log(`Server berjalan pada ${server.info.uri}`);
19 | };
20 |
21 |
22 | init();
--------------------------------------------------------------------------------
/calculate_circle.js:
--------------------------------------------------------------------------------
1 | function circle_area(radius) {
2 | return Math.PI * Math.pow(radius, 2);
3 | }
4 |
5 | function circle_perimeter(radius) {
6 | return 2 * Math.PI * radius;
7 | }
8 |
9 | module.exports = {
10 | circle_area,
11 | circle_perimeter,
12 | }
--------------------------------------------------------------------------------
/colorize-text.js:
--------------------------------------------------------------------------------
1 | const colorize = (...args) => ({
2 | black: `\x1b[30m${args.join(' ')}`,
3 | red: `\x1b[31m${args.join(' ')}`,
4 | green: `\x1b[32m${args.join(' ')}`,
5 | yellow: `\x1b[33m${args.join(' ')}`,
6 | blue: `\x1b[34m${args.join(' ')}`,
7 | magenta: `\x1b[35m${args.join(' ')}`,
8 | cyan: `\x1b[36m${args.join(' ')}`,
9 | white: `\x1b[37m${args.join(' ')}`,
10 | bgBlack: `\x1b[40m${args.join(' ')}\x1b[0m`,
11 | bgRed: `\x1b[41m${args.join(' ')}\x1b[0m`,
12 | bgGreen: `\x1b[42m${args.join(' ')}\x1b[0m`,
13 | bgYellow: `\x1b[43m${args.join(' ')}\x1b[0m`,
14 | bgBlue: `\x1b[44m${args.join(' ')}\x1b[0m`,
15 | bgMagenta: `\x1b[45m${args.join(' ')}\x1b[0m`,
16 | bgCyan: `\x1b[46m${args.join(' ')}\x1b[0m`,
17 | bgWhite: `\x1b[47m${args.join(' ')}\x1b[0m`
18 | });
19 |
20 | // console.log(colorize('foo').red); // 'foo' (red letters)
21 | // console.log(colorize('foo', 'bar').bgBlue); // 'foo bar' (blue background)
22 | // console.log(colorize(colorize('foo').yellow, colorize('foo').green).bgWhite);
23 |
--------------------------------------------------------------------------------
/express-vercel/.github/workflows/.gitignore:
--------------------------------------------------------------------------------
1 | .vercel
2 |
--------------------------------------------------------------------------------
/express-vercel/.github/workflows/github-ci-vercel-preview.yml:
--------------------------------------------------------------------------------
1 | name: GitHub Actions Vercel Production Deployment
2 | env:
3 | VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
4 | VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
5 | on:
6 | push:
7 | branches-ignore:
8 | - main
9 | jobs:
10 | Deploy-Preview:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v2
14 | - name: Install Vercel CLI
15 | run: npm install --global vercel@latest
16 | - name: Pull Vercel Environment Information
17 | run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }}
18 | - name: Build Project Artifacts
19 | run: vercel build --token=${{ secrets.VERCEL_TOKEN }}
20 | - name: Deploy Project Artifacts to Vercel
21 | run: vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }}
22 |
--------------------------------------------------------------------------------
/express-vercel/.github/workflows/github-ci-vercel-production.yml:
--------------------------------------------------------------------------------
1 | name: GitHub Actions Vercel Preview Deployment
2 | env:
3 | VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
4 | VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
5 | on:
6 | push:
7 | branches-ignore:
8 | - main
9 | jobs:
10 | Deploy-Preview:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v2
14 | - name: Install Vercel CLI
15 | run: npm install --global vercel@latest
16 | - name: Pull Vercel Environment Information
17 | run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }}
18 | - name: Build Project Artifacts
19 | run: vercel build --token=${{ secrets.VERCEL_TOKEN }}
20 | - name: Deploy Project Artifacts to Vercel
21 | run: vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }}
22 |
--------------------------------------------------------------------------------
/express-vercel/.github/workflows/node.js.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Node.js CI
5 |
6 | on:
7 | push:
8 | branches: [ "main" ]
9 | pull_request:
10 | branches: [ "main" ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | strategy:
18 | matrix:
19 | node-version: [14.x, 16.x, 18.x]
20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
21 |
22 | steps:
23 | - uses: actions/checkout@v3
24 | - name: Use Node.js ${{ matrix.node-version }}
25 | uses: actions/setup-node@v3
26 | with:
27 | node-version: ${{ matrix.node-version }}
28 | cache: 'npm'
29 | - run: npm ci
30 | - run: npm run build --if-present
31 | - run: npm test
32 |
--------------------------------------------------------------------------------
/express-vercel/.gitignore:
--------------------------------------------------------------------------------
1 | logs
2 | *.log
3 | npm-debug.log*
4 | yarn-debug.log*
5 | yarn-error.log*
6 | lerna-debug.log*
7 |
8 | node_modules/
9 | jspm_packages/
10 |
--------------------------------------------------------------------------------
/express-vercel/controllers/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | index: async (req, res, next) => {
3 | try {
4 | return res.status(200).json({
5 | status: true,
6 | message: "Get Data Successfully"
7 | });
8 | } catch (error) {
9 | next(error)
10 | }
11 | },
12 |
13 | delete: async (req, res, next) => {
14 | try {
15 | return res.status(200).json({
16 | status: true,
17 | message: "Deleted User Data Successfully"
18 | });
19 | } catch (error) {
20 | next(error)
21 | }
22 | },
23 | };
24 |
--------------------------------------------------------------------------------
/express-vercel/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const app = express();
3 | const product = require('./routes');
4 |
5 | app.use(express.json({extended: false}));
6 |
7 | // app.use('/api/product', product);
8 | app.use('/', product)
9 |
10 | const PORT = process.env.PORT || 8000;
11 | app.listen(PORT, () => console.log(`Server is running in port ${PORT}`));
12 |
13 |
14 |
--------------------------------------------------------------------------------
/express-vercel/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "express-vercel",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "repository": "git@gitlab.com:gricowijaya/express-vercel.git",
6 | "author": "Gede Rico Wijaya ",
7 | "license": "MIT",
8 | "scripts": {
9 | "start": "node index.js",
10 | "dev": "nodemon index.js",
11 | "test": "jest"
12 | },
13 | "dependencies": {
14 | "express": "^4.18.2"
15 | },
16 | "devDependencies": {
17 | "jest": "^29.2.1",
18 | "nodemon": "^2.0.20",
19 | "supertest": "^6.3.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/express-vercel/readme.md:
--------------------------------------------------------------------------------
1 | EXPRESS APP with CI/CD
2 |
3 | Deployed using vercel
4 |
--------------------------------------------------------------------------------
/express-vercel/routes/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const router = express.Router();
3 | const controller = require('../controllers/index')
4 |
5 | router.get('/', controller.index);
6 |
7 | router.get('/delete-user', controller.delete);
8 |
9 | module.exports = router;
10 |
--------------------------------------------------------------------------------
/express-vercel/tests/test.spec.js:
--------------------------------------------------------------------------------
1 | const product = require('../controllers/index.js');
2 | const mockRequest = (body={}) => ({ body })
3 | const mockResponse = () => {
4 | const res = {}
5 | res.json = jest.fn().mockReturnValue(res);
6 | res.status = jest.fn().mockReturnValue(res);
7 | return res;
8 | }
9 |
10 | // endpoint GET/
11 | describe('product.index.function', () => {
12 | // case if success
13 | test('res.json called with {status: true, message: "Get Data Successfully"}', done => {
14 | const req = mockRequest();
15 | const res = mockResponse();
16 | product.index(req, res);
17 | expect(res.status).toBeCalledWith(200); // expected status
18 | expect(res.json).toBeCalledWith({
19 | status: true,
20 | message: "Get Data Successfully"
21 | });
22 | done();
23 | });
24 | });
25 |
26 | // // endpoint that delete the sum
27 | describe('product.delete.function', () => {
28 | // case if success
29 | test('res.json called with {status: true, message: "Deleted User Data Successfully"}', done => {
30 | const req = mockRequest();
31 | const res = mockResponse();
32 | product.delete(req, res);
33 | expect(res.status).toBeCalledWith(200); // expected status
34 | expect(res.json).toBeCalledWith({
35 | status: true,
36 | message: "Deleted User Data Successfully"
37 | });
38 | done();
39 | });
40 | });
41 |
--------------------------------------------------------------------------------
/generate-uuid.js:
--------------------------------------------------------------------------------
1 | const crypto = require('crypto');
2 |
3 | const UUIDGeneratorNode = () =>
4 | ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
5 | (c ^ (crypto.randomBytes(1)[0] & (15 >> (c / 4)))).toString(16)
6 | );
7 |
--------------------------------------------------------------------------------
/google-play-scraper/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const gplay = require('google-play-scraper')
3 | const app = express()
4 |
5 | /*
6 | | ------------------------------------------------------
7 | | e.g http://localhost:3000?id=com.alibaba.aliexpresshd
8 | |
9 | | return json
10 | | {
11 | | "title": "AliExpress",
12 | | "version": "8.55.1"
13 | | }
14 | | ------------------------------------------------------
15 | */
16 | https: app.get('/', async (req, res) => {
17 | const { id } = req.query
18 | if (!id) return res.status(400).send('No id provided')
19 |
20 | try {
21 | const { title, version } = await gplay.app({ appId: id })
22 |
23 | res.send({ title, version })
24 | } catch (err) {
25 | res.status(500).send(err.message)
26 | }
27 | })
28 |
29 | app.listen(3000, () => console.log('Example app listening on port 3000!'))
30 |
--------------------------------------------------------------------------------
/google-play-scraper/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "google-play-scraper-api",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "aryarfani",
10 | "license": "ISC",
11 | "dependencies": {
12 | "axios": "^0.27.2",
13 | "express": "^4.18.1",
14 | "google-play-scraper": "git+https://github.com/facundoolano/google-play-scraper"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/hello-world.js:
--------------------------------------------------------------------------------
1 | http.createServer(function (request, response) {
2 | // Send the HTTP header
3 | // HTTP Status: 200 : OK
4 | // Content Type: text/plain
5 | response.writeHead(200, {'Content-Type': 'text/plain'});
6 |
7 | // Send the response body as "Hello World"
8 | response.end('Hello World\n');
9 | }).listen(8081);
10 |
11 | // Console will print the message
12 | console.log('Server running at http://127.0.0.1:8081/');
--------------------------------------------------------------------------------
/matrix.js:
--------------------------------------------------------------------------------
1 | const matcal = (matrix) => {
2 | let him = [];
3 | for (j = 0; j < matrix.length; j++) {
4 | awal = matrix[j][0];
5 | for (i = 0; i < matrix[0].length - 1; i++) {
6 | let n = i + 1;
7 | awal = awal.map((res, i) => res + matrix[j][n][i]);
8 | }
9 | him.push(awal);
10 | }
11 | return him;
12 | };
13 |
14 | const example = [
15 | [
16 | [1, 1, 1],
17 | [3, 4, 5],
18 | [6, 7, 8],
19 | ],
20 | [
21 | [1 / 5, 1 / 4, 1 / 3],
22 | [1, 1, 1],
23 | [4, 5, 6],
24 | ],
25 | [
26 | [1 / 8, 1 / 7, 1 / 6],
27 | [1 / 6, 1 / 5, 1 / 4],
28 | [1, 1, 1],
29 | ],
30 | ];
31 |
32 | const result = matcal(example);
33 | console.log(result);
34 |
--------------------------------------------------------------------------------
/phonenumberbyarray-farizz.js:
--------------------------------------------------------------------------------
1 | function createPhoneNumber(numbers) {
2 | var result = "";
3 | numbers.forEach((data, index) => {
4 | if (index == 0) {
5 | result += `(${data}`
6 | } else if (index == 2) {
7 | result += `${data}) `
8 | } else if (index == 5) {
9 | result += `${data}-`
10 | } else {
11 | result += data
12 | }
13 | })
14 | return result
15 | }
16 | createPhoneNumber([1,2,3,4,5,6,7,8,9,0])
17 |
--------------------------------------------------------------------------------
/random_number.js:
--------------------------------------------------------------------------------
1 | if(!process.argv[2] || isNaN(parseInt(process.argv[2]))){
2 | throw new Error('Invalid Argument');
3 | } else {
4 | console.log(Math.floor(Math.random() * Math.pow(process.argv[2], parseInt(1))));
5 | }
6 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | // server.js
2 | var fs = require('fs');
3 | var app = require('./app');
4 | var httpPort = process.env.PORT || 8000;
5 | var httpsPort = process.env.PORT || 8100;
6 | var http = require('http');
7 | // var https = require('https');
8 | // var privateKey = fs.readFileSync('certificates/private.key', 'utf8');
9 | // var certificate = fs.readFileSync('certificates/certificate.crt', 'utf8');
10 | // var credentials = {key: privateKey, cert: certificate};
11 | var httpServer = http.createServer(app);
12 | // var httpsServer = https.createServer(credentials, app);
13 | const winston = require('winston');
14 |
15 | const logger = winston.createLogger({
16 | transports: [
17 | new winston.transports.File({
18 | filename: './logs/error.log',
19 | level: 'error',
20 | format: winston.format.json()
21 | }),
22 | new winston.transports.File({
23 | filename: './logs/success.log',
24 | level: 'info',
25 | format: winston.format.json()
26 | }),
27 | new winston.transports.Http({
28 | filename: './logs/http.log',
29 | level: 'error',
30 | format: winston.format.json()
31 | }),
32 | new winston.transports.Console({
33 | filename: './logs/console.log',
34 | level: 'error',
35 | format: winston.format.json()
36 | })
37 | ]
38 | });
39 |
40 | // LOGGER IN PRODUCTION
41 | if (process.env.NODE_ENV !== 'production') {
42 | logger.add(new winston.transports.Console({
43 | format: winston.format.simple(),
44 | }));
45 | }
46 |
47 | // Socket.io server
48 | const { Server } = require('socket.io');
49 | const io = new Server(httpServer, {
50 | cors: {
51 | origin: ["*", "http://localhost", "http://localhost:8100"],
52 | methods: ["GET", "POST"],
53 | transports: ['websocket', 'polling'],
54 | credentials: true
55 | },
56 | allowEIO3: true
57 | });
58 | const socketEvents = require('../socket/events')(io);
59 | socketEvents;
60 | // Load exchange rates
61 | var money = require('../config/money');
62 | money.getRates();
63 | // Run script
64 | var runScript = require('./scripts/runScript');
65 | runScript.run();
66 |
67 | // Starting Server (HTTP)
68 | httpServer.listen(httpPort, function() {
69 | console.log('LoanApp API (HTTP) Development Server listening on port ' + httpPort);
70 | });
71 |
72 | // // Starting Server (HTTPS)
73 | // httpsServer.listen(httpsPort, function() {
74 | // console.log('LoanApp API (HTTPS) Development Server listening on port ' + httpsPort);
75 | // });
76 |
77 | // Default launch URL
78 | app.use('/', function(req, res) {
79 | res.status(200).send({ message: 'LoanApp API Development Server is Running' });
80 | });
81 |
82 |
--------------------------------------------------------------------------------
/service-user/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FirmanKurniawan/NodeJS-Projects/f520cdd8e4e1163e3414ba5a6692b24ecad95462/service-user/.DS_Store
--------------------------------------------------------------------------------
/service-user/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | .env
--------------------------------------------------------------------------------
/service-user/app.js:
--------------------------------------------------------------------------------
1 | require("dotenv").config();
2 | const express = require('express');
3 | const path = require('path');
4 | const cookieParser = require('cookie-parser');
5 | const logger = require('morgan');
6 |
7 | const indexRouter = require('./routes/index');
8 | const usersRouter = require('./routes/users');
9 | const refreshTokensRouter = require('./routes/refreshTokens');
10 |
11 | const app = express();
12 |
13 | app.use(logger('dev'));
14 | app.use(express.json());
15 | app.use(express.urlencoded({ extended: false }));
16 | app.use(cookieParser());
17 | app.use(express.static(path.join(__dirname, 'public')));
18 |
19 | app.use('/', indexRouter);
20 | app.use('/users', usersRouter);
21 | app.use('/refresh-tokens', refreshTokensRouter);
22 |
23 | module.exports = app;
24 |
--------------------------------------------------------------------------------
/service-user/bin/www:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * Module dependencies.
5 | */
6 |
7 | var app = require('../app');
8 | var debug = require('debug')('service-user:server');
9 | var http = require('http');
10 |
11 | /**
12 | * Get port from environment and store in Express.
13 | */
14 |
15 | var port = normalizePort(process.env.PORT || '3000');
16 | app.set('port', port);
17 |
18 | /**
19 | * Create HTTP server.
20 | */
21 |
22 | var server = http.createServer(app);
23 |
24 | /**
25 | * Listen on provided port, on all network interfaces.
26 | */
27 |
28 | server.listen(port);
29 | server.on('error', onError);
30 | server.on('listening', onListening);
31 |
32 | /**
33 | * Normalize a port into a number, string, or false.
34 | */
35 |
36 | function normalizePort(val) {
37 | var port = parseInt(val, 10);
38 |
39 | if (isNaN(port)) {
40 | // named pipe
41 | return val;
42 | }
43 |
44 | if (port >= 0) {
45 | // port number
46 | return port;
47 | }
48 |
49 | return false;
50 | }
51 |
52 | /**
53 | * Event listener for HTTP server "error" event.
54 | */
55 |
56 | function onError(error) {
57 | if (error.syscall !== 'listen') {
58 | throw error;
59 | }
60 |
61 | var bind = typeof port === 'string'
62 | ? 'Pipe ' + port
63 | : 'Port ' + port;
64 |
65 | // handle specific listen errors with friendly messages
66 | switch (error.code) {
67 | case 'EACCES':
68 | console.error(bind + ' requires elevated privileges');
69 | process.exit(1);
70 | break;
71 | case 'EADDRINUSE':
72 | console.error(bind + ' is already in use');
73 | process.exit(1);
74 | break;
75 | default:
76 | throw error;
77 | }
78 | }
79 |
80 | /**
81 | * Event listener for HTTP server "listening" event.
82 | */
83 |
84 | function onListening() {
85 | var addr = server.address();
86 | var bind = typeof addr === 'string'
87 | ? 'pipe ' + addr
88 | : 'port ' + addr.port;
89 | debug('Listening on ' + bind);
90 | }
91 |
--------------------------------------------------------------------------------
/service-user/config/config.js:
--------------------------------------------------------------------------------
1 | require("dotenv").config();
2 | const { DB_USERNAME, DB_PASSWORD, DB_HOST, DB_NAME } = process.env;
3 |
4 | module.exports = {
5 | development: {
6 | username: DB_USERNAME,
7 | password: DB_PASSWORD,
8 | database: DB_NAME,
9 | host: DB_HOST,
10 | dialect: "mysql",
11 | },
12 | test: {
13 | username: DB_USERNAME,
14 | password: DB_PASSWORD,
15 | database: DB_NAME,
16 | host: DB_HOST,
17 | dialect: "mysql",
18 | },
19 | production: {
20 | username: DB_USERNAME,
21 | password: DB_PASSWORD,
22 | database: DB_NAME,
23 | host: DB_HOST,
24 | dialect: "mysql",
25 | },
26 | };
27 |
--------------------------------------------------------------------------------
/service-user/controllers/RefreshTokenController.js:
--------------------------------------------------------------------------------
1 | const { RefreshToken, User } = require("../models");
2 | const Validator = require("fastest-validator");
3 | const v = new Validator();
4 |
5 | module.exports = {
6 | store: async (req, res) => {
7 | const { user_id, refresh_token } = req.body;
8 |
9 | const rules = {
10 | refresh_token: "string|empty:false",
11 | user_id: "number|empty:false",
12 | };
13 |
14 | const validate = v.validate(req.body, rules);
15 | if (validate.length) {
16 | return res.status(422).json({
17 | status: "error",
18 | message: validate,
19 | });
20 | }
21 |
22 | const user = await User.findByPk(user_id);
23 |
24 | if (!user) {
25 | return res.status(404).json({
26 | status: "error",
27 | message: "user not found",
28 | });
29 | }
30 |
31 | const createdRefreshToken = await RefreshToken.create({
32 | token: refresh_token,
33 | user_id,
34 | });
35 |
36 | return res.json({
37 | status: "success",
38 | message: "successfully create token",
39 | data: {
40 | id: createdRefreshToken.id,
41 | },
42 | });
43 | },
44 | getToken: async (req, res) => {
45 | const { refresh_token } = req.query;
46 | const token = await RefreshToken.findOne({
47 | where: {
48 | token: refresh_token,
49 | },
50 | });
51 |
52 | if (!token) {
53 | return res.status(404).json({
54 | status: "error",
55 | message: "invalid token",
56 | });
57 | }
58 |
59 | return res.json({
60 | status: "success",
61 | token,
62 | });
63 | },
64 | };
65 |
--------------------------------------------------------------------------------
/service-user/controllers/UserController.js:
--------------------------------------------------------------------------------
1 | const { User, RefreshToken } = require("../models");
2 | const bcrypt = require("bcrypt");
3 | const validator = require("fastest-validator");
4 | const v = new validator();
5 |
6 | module.exports = {
7 | index: async (req, res) => {
8 | const userIds = req.query.user_ids || [];
9 |
10 | const sqlOptions = {
11 | attributes: ["id", "name", "email", "profession", "role", "avatar"],
12 | };
13 |
14 | if (userIds.length) {
15 | sqlOptions.where = {
16 | id: userIds,
17 | };
18 | }
19 |
20 | const users = await User.findAll(sqlOptions);
21 |
22 | return res.json({
23 | status: "success",
24 | message: "get list user",
25 | data: users,
26 | });
27 | },
28 | show: async (req, res) => {
29 | const { id } = req.params;
30 | const user = await User.findByPk(id, {
31 | attributes: ["id", "name", "email", "role", "profession", "avatar"],
32 | });
33 | if (!user) {
34 | return res.status(404).json({
35 | status: "error",
36 | message: "user not found",
37 | });
38 | }
39 | return res.json({
40 | status: "success",
41 | message: "get user info",
42 | data: user,
43 | });
44 | },
45 | register: async (req, res) => {
46 | const rules = {
47 | name: "string|empty:false",
48 | email: "email|empty:false",
49 | password: "string|min:6",
50 | profession: "string|optional",
51 | };
52 |
53 | const validate = v.validate(req.body, rules);
54 | if (validate.length) {
55 | return res.status(422).json({
56 | status: "error",
57 | message: validate,
58 | });
59 | }
60 |
61 | const user = await User.findOne({
62 | where: {
63 | email: req.body.email,
64 | },
65 | });
66 |
67 | if (user) {
68 | return res.status(409).json({
69 | status: "error",
70 | message: "email already exist",
71 | });
72 | }
73 |
74 | const password = await bcrypt.hash(req.body.password, 10);
75 |
76 | const data = {
77 | password,
78 | email: req.body.email,
79 | name: req.body.name,
80 | profession: req.body.profession,
81 | role: "student",
82 | };
83 |
84 | const createUser = await User.create(data);
85 |
86 | return res.status(201).json({
87 | status: "success",
88 | message: "successfully register new user",
89 | data: {
90 | id: createUser.id,
91 | },
92 | });
93 | },
94 | login: async (req, res) => {
95 | const rules = {
96 | email: "email|empty:false",
97 | password: "string|min:6",
98 | };
99 |
100 | const validate = v.validate(req.body, rules);
101 | if (validate.length) {
102 | return res.status(422).json({
103 | status: "error",
104 | message: validate,
105 | });
106 | }
107 |
108 | const user = await User.findOne({ where: { email: req.body.email } });
109 |
110 | if (!user) {
111 | return res.status(404).json({
112 | status: "error",
113 | message: "user not found",
114 | });
115 | }
116 |
117 | const isValidPassword = await bcrypt.compare(
118 | req.body.password,
119 | user.password
120 | );
121 |
122 | if (!isValidPassword) {
123 | return res.status(404).json({
124 | status: "error",
125 | message: "wrong password",
126 | });
127 | }
128 |
129 | return res.json({
130 | status: "success",
131 | message: "login success",
132 | data: {
133 | id: user.id,
134 | name: user.name,
135 | email: user.email,
136 | role: user.role,
137 | avatar: user.avatar,
138 | profession: user.profession,
139 | },
140 | });
141 | },
142 | update: async (req, res) => {
143 | const rules = {
144 | name: "string|empty:false",
145 | email: "email|empty:false",
146 | password: "string|min:6",
147 | profession: "string|optional",
148 | avatar: "string|optional",
149 | };
150 |
151 | const validate = v.validate(req.body, rules);
152 | if (validate.length) {
153 | return res.status(422).json({
154 | status: "error",
155 | message: validate,
156 | });
157 | }
158 |
159 | const { id } = req.params;
160 |
161 | const user = await User.findByPk(id);
162 |
163 | if (!user) {
164 | return res.status(404).json({
165 | status: "error",
166 | message: "user not found",
167 | });
168 | }
169 |
170 | const { email } = req.body;
171 |
172 | if (email) {
173 | const checkEmail = await User.findOne({ where: { email } });
174 | if (checkEmail && email !== user.email) {
175 | return res.status(409).json({
176 | status: "error",
177 | message: "email already exist",
178 | });
179 | }
180 | }
181 |
182 | const password = await bcrypt.hash(req.body.password, 10);
183 | const { name, profession, avatar } = req.body;
184 |
185 | const updatedUser = await user.update({
186 | email,
187 | password,
188 | name,
189 | profession,
190 | avatar,
191 | });
192 |
193 | return res.json({
194 | status: "success",
195 | message: "successfully update user profile",
196 | data: {
197 | id: updatedUser.id,
198 | name: updatedUser.name,
199 | email: updatedUser.email,
200 | profession: updatedUser.profession,
201 | avatar: updatedUser.avatar,
202 | },
203 | });
204 | },
205 | logout: async (req, res) => {
206 | const { user_id } = req.body;
207 |
208 | const user = await User.findByPk(user_id);
209 |
210 | if (!user) {
211 | return res.status(404).json({
212 | status: "error",
213 | message: "user not found",
214 | });
215 | }
216 |
217 | await RefreshToken.destroy({
218 | where: { user_id },
219 | });
220 |
221 | return res.json({
222 | status: "success",
223 | message: "refresh token deleted successfully",
224 | });
225 | },
226 | };
227 |
--------------------------------------------------------------------------------
/service-user/env.example:
--------------------------------------------------------------------------------
1 | PORT=8081
2 |
3 | DB_HOST=
4 | DB_USERNAME=
5 | DB_PASSWORD=
6 | DB_NAME=
--------------------------------------------------------------------------------
/service-user/migrations/20220124144247-create_users_table.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = {
4 | async up(queryInterface, Sequelize) {
5 | await queryInterface.createTable("users", {
6 | id: {
7 | type: Sequelize.INTEGER,
8 | primaryKey: true,
9 | autoIncrement: true,
10 | allowNull: false,
11 | },
12 | name: {
13 | type: Sequelize.STRING,
14 | allowNull: false,
15 | },
16 | profession: {
17 | type: Sequelize.STRING,
18 | allowNull: true,
19 | },
20 | avatar: {
21 | type: Sequelize.STRING,
22 | allowNull: true,
23 | },
24 | role: {
25 | type: Sequelize.ENUM,
26 | values: ["admin", "student"],
27 | allowNull: false,
28 | },
29 | email: {
30 | type: Sequelize.STRING,
31 | allowNull: false,
32 | },
33 | password: {
34 | type: Sequelize.STRING,
35 | allowNull: false,
36 | },
37 | created_at: {
38 | type: Sequelize.DATE,
39 | allowNull: false,
40 | },
41 | updated_at: {
42 | type: Sequelize.DATE,
43 | allowNull: false,
44 | },
45 | });
46 |
47 | await queryInterface.addConstraint("users", {
48 | type: "unique",
49 | fields: ["email"],
50 | name: "custom_unique_constraint_email",
51 | });
52 | },
53 |
54 | async down(queryInterface, Sequelize) {
55 | await queryInterface.dropTable("users");
56 | },
57 | };
58 |
--------------------------------------------------------------------------------
/service-user/migrations/20220124144311-create_refresh_token_table.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = {
4 | async up(queryInterface, Sequelize) {
5 | await queryInterface.createTable("refresh_tokens", {
6 | id: {
7 | type: Sequelize.INTEGER,
8 | primaryKey: true,
9 | autoIncrement: true,
10 | allowNull: false,
11 | },
12 | token: {
13 | type: Sequelize.TEXT,
14 | allowNull: false,
15 | },
16 | user_id: {
17 | type: Sequelize.INTEGER,
18 | allowNull: false,
19 | },
20 | created_at: {
21 | type: Sequelize.DATE,
22 | allowNull: false,
23 | },
24 | updated_at: {
25 | type: Sequelize.DATE,
26 | allowNull: false,
27 | },
28 | });
29 |
30 | await queryInterface.addConstraint("refresh_tokens", {
31 | type: "foreign key",
32 | fields: ["user_id"],
33 | name: "custom_fkey_constraint_user_id",
34 | references: {
35 | table: "users",
36 | field: "id",
37 | },
38 | });
39 | },
40 |
41 | async down(queryInterface, Sequelize) {
42 | await queryInterface.dropTable("refresh_tokens");
43 | },
44 | };
45 |
--------------------------------------------------------------------------------
/service-user/models/RefreshToken.js:
--------------------------------------------------------------------------------
1 | module.exports = (Sequelize, DataTypes) => {
2 | const RefreshToken = Sequelize.define(
3 | "RefreshToken",
4 | {
5 | id: {
6 | type: DataTypes.INTEGER,
7 | primaryKey: true,
8 | autoIncrement: true,
9 | allowNull: false,
10 | },
11 | token: {
12 | type: DataTypes.TEXT,
13 | allowNull: false,
14 | },
15 | user_id: {
16 | type: DataTypes.INTEGER,
17 | allowNull: false,
18 | },
19 | createdAt: {
20 | field: "created_at",
21 | type: DataTypes.DATE,
22 | allowNull: false,
23 | },
24 | updatedAt: {
25 | field: "updated_at",
26 | type: DataTypes.DATE,
27 | allowNull: false,
28 | },
29 | },
30 | {
31 | tableName: "refresh_tokens",
32 | timestamp: true,
33 | }
34 | );
35 |
36 | return RefreshToken;
37 | };
38 |
--------------------------------------------------------------------------------
/service-user/models/User.js:
--------------------------------------------------------------------------------
1 | module.exports = (Sequelize, DataTypes) => {
2 | const User = Sequelize.define(
3 | "User",
4 | {
5 | id: {
6 | type: DataTypes.INTEGER,
7 | primaryKey: true,
8 | autoIncrement: true,
9 | allowNull: false,
10 | },
11 | name: {
12 | type: DataTypes.STRING,
13 | allowNull: false,
14 | },
15 | profession: {
16 | type: DataTypes.STRING,
17 | allowNull: true,
18 | },
19 | avatar: {
20 | type: DataTypes.STRING,
21 | allowNull: true,
22 | },
23 | role: {
24 | type: DataTypes.ENUM,
25 | values: ["admin", "student"],
26 | allowNull: false,
27 | defaultValue: "student",
28 | },
29 | email: {
30 | type: DataTypes.STRING,
31 | allowNull: false,
32 | unique: true,
33 | },
34 | password: {
35 | type: DataTypes.STRING,
36 | allowNull: false,
37 | },
38 | createdAt: {
39 | field: "created_at",
40 | type: DataTypes.DATE,
41 | allowNull: false,
42 | },
43 | updatedAt: {
44 | field: "updated_at",
45 | type: DataTypes.DATE,
46 | allowNull: false,
47 | },
48 | },
49 | {
50 | tableName: "users",
51 | timestamp: true
52 | }
53 | );
54 |
55 | return User;
56 | };
57 |
--------------------------------------------------------------------------------
/service-user/models/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs');
4 | const path = require('path');
5 | const Sequelize = require('sequelize');
6 | const basename = path.basename(__filename);
7 | const env = process.env.NODE_ENV || 'development';
8 | const config = require(__dirname + '/../config/config.js')[env];
9 | const db = {};
10 |
11 | let sequelize;
12 | if (config.use_env_variable) {
13 | sequelize = new Sequelize(process.env[config.use_env_variable], config);
14 | } else {
15 | sequelize = new Sequelize(config.database, config.username, config.password, config);
16 | }
17 |
18 | fs
19 | .readdirSync(__dirname)
20 | .filter(file => {
21 | return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
22 | })
23 | .forEach(file => {
24 | const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
25 | db[model.name] = model;
26 | });
27 |
28 | Object.keys(db).forEach(modelName => {
29 | if (db[modelName].associate) {
30 | db[modelName].associate(db);
31 | }
32 | });
33 |
34 | db.sequelize = sequelize;
35 | db.Sequelize = Sequelize;
36 |
37 | module.exports = db;
38 |
--------------------------------------------------------------------------------
/service-user/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "service-user",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "nodemon ./bin/www"
7 | },
8 | "dependencies": {
9 | "bcrypt": "^5.0.1",
10 | "cookie-parser": "~1.4.4",
11 | "debug": "~2.6.9",
12 | "dotenv": "^14.2.0",
13 | "express": "~4.16.1",
14 | "fastest-validator": "^1.12.0",
15 | "morgan": "~1.9.1",
16 | "mysql2": "^2.3.3",
17 | "sequelize": "^6.14.0",
18 | "sequelize-cli": "^6.4.1"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/service-user/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Express
5 |
6 |
7 |
8 |
9 | Express
10 | Welcome to Express
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/service-user/public/stylesheets/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 50px;
3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
4 | }
5 |
6 | a {
7 | color: #00B7FF;
8 | }
9 |
--------------------------------------------------------------------------------
/service-user/routes/index.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var router = express.Router();
3 |
4 | /* GET home page. */
5 | router.get('/', function(req, res, next) {
6 | res.render('index', { title: 'Express' });
7 | });
8 |
9 | module.exports = router;
10 |
--------------------------------------------------------------------------------
/service-user/routes/refreshTokens.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const router = express.Router();
3 |
4 | const { store, getToken } = require("../controllers/RefreshTokenController");
5 |
6 | router.post("/", store);
7 | router.get("/", getToken);
8 |
9 | module.exports = router;
10 |
--------------------------------------------------------------------------------
/service-user/routes/users.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const router = express.Router();
3 |
4 | const {
5 | register,
6 | login,
7 | update,
8 | show,
9 | index,
10 | logout,
11 | } = require("../controllers/UserController");
12 |
13 | router.get("/", index);
14 | router.get("/:id", show);
15 | router.put("/:id", update);
16 | router.post("/register", register);
17 | router.post("/login", login);
18 | router.post("/logout", logout);
19 |
20 | module.exports = router;
21 |
--------------------------------------------------------------------------------
/service-user/seeders/20220124150408-user_seeders.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const bcrypt = require("bcrypt");
3 |
4 | module.exports = {
5 | async up(queryInterface, Sequelize) {
6 | await queryInterface.bulkInsert(
7 | "users",
8 | [
9 | {
10 | name: "BrondoL",
11 | profession: "Admin web",
12 | role: "admin",
13 | email: "nabilunited2@gmail.com",
14 | password: await bcrypt.hash("rahasia", 10),
15 | created_at: new Date(),
16 | updated_at: new Date(),
17 | },
18 | {
19 | name: "Wawik",
20 | profession: "Front End Developer",
21 | role: "student",
22 | email: "wawik@gmail.com",
23 | password: await bcrypt.hash("rahasia", 10),
24 | created_at: new Date(),
25 | updated_at: new Date(),
26 | },
27 | ],
28 | {}
29 | );
30 | },
31 |
32 | async down(queryInterface, Sequelize) {
33 | await queryInterface.bulkDelete("users", null, {});
34 | },
35 | };
36 |
--------------------------------------------------------------------------------
/static-file-server.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const http = require('http');
3 |
4 | http.createServer((req, res) => {
5 | fs.readFile(__dirname + req.url, (err, data) => {
6 | if (err) {
7 | res.writeHead(404, { 'Content-Type': 'text/html' });
8 | res.end('404: File not found');
9 | } else {
10 | res.writeHead(200, { 'Content-Type': 'text/html' });
11 | res.end(data);
12 | }
13 | });
14 | }).listen(8000);
15 |
--------------------------------------------------------------------------------