diff --git a/client/.env.example b/client/.env.example new file mode 100644 index 0000000..eb92f25 --- /dev/null +++ b/client/.env.example @@ -0,0 +1,2 @@ +# Google OAuth client ID +VITE_GOOGLE_CLIENT_ID=your-google-client-id-here \ No newline at end of file diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 0000000..1e81c07 --- /dev/null +++ b/client/.gitignore @@ -0,0 +1,25 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +.env +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/client/README.md b/client/README.md new file mode 100644 index 0000000..7059a96 --- /dev/null +++ b/client/README.md @@ -0,0 +1,12 @@ +# React + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project. diff --git a/client/eslint.config.js b/client/eslint.config.js new file mode 100644 index 0000000..cee1e2c --- /dev/null +++ b/client/eslint.config.js @@ -0,0 +1,29 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{js,jsx}'], + extends: [ + js.configs.recommended, + reactHooks.configs['recommended-latest'], + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + parserOptions: { + ecmaVersion: 'latest', + ecmaFeatures: { jsx: true }, + sourceType: 'module', + }, + }, + rules: { + 'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }], + }, + }, +]) diff --git a/client/index.html b/client/index.html new file mode 100644 index 0000000..967a50f --- /dev/null +++ b/client/index.html @@ -0,0 +1,27 @@ + + +
+ + + + + ++ Interact with your Digimon in My Digimon page. +
+
+ Power: {lastInteracted.power}
+
+ Hunger: {lastInteracted.hunger}
+
+ Happiness: {lastInteracted.happiness}
+
+ Attribute: {lastInteracted.attribute}
+
Loading market...
; + if (error) returnError loading Digimon: {error}
; + + return ( +
+ Level: {digimon.level}
+
+ Attribute: {digimon.attribute || "-"}
+
No more Digimon.
} +Loading digimons...
; + if (error) returnError: {error}
; + + return ( + <> +
+ Power: {digimon.power}
+ Hunger: {digimon.hunger}
+ Happiness: {digimon.happiness}
+ Attribute: {digimon.attribute}
+
No Digimons found.
+ )} +Loading profile...
; + if (error) return{error}
; + + return ( + <> ++ Name: {user?.name || "Unknown User"} +
++ Email: {user?.email || "unknown@email.com"} +
++ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 | 1x +1x +1x +1x +1x +1x + +1x + +1x +1x +1x +1x + + + + + + + + + + + + + + + + + + +1x +1x +1x +1x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +1x +1x + + + + + + + +1x + + + + | const { hashPassword, comparePassword } = require("../helpers/jwt.helper");
+const { User } = require("../models");
+const { OAuth2Client } = require("google-auth-library");
+const client = new OAuth2Client(process.env.GOOGLE_CLIENT_ID);
+const jwt = require("jsonwebtoken");
+const bcrypt = require("bcryptjs");
+
+module.exports = {
+ async register(req, res, next) {
+ try {
+ const { name, email, password } = req.body;
+ Eif (!name || !email || !password) {
+ return res
+ .status(400)
+ .json({ error: "Name, email, and password are required" });
+ }
+ const hashed = await hashPassword(password);
+ const user = await User.create({ name, email, password: hashed });
+ res.status(201).json({ id: user.id, email: user.email });
+ } catch (err) {
+ if (err.name === "SequelizeUniqueConstraintError") {
+ return res.status(400).json({ error: "Email already used" });
+ }
+ if (err.name === "SequelizeValidationError") {
+ return res.status(400).json({ error: err.errors[0].message });
+ }
+ next(err);
+ }
+ },
+
+ async login(req, res, next) {
+ try {
+ const { email, password } = req.body;
+ Eif (!email || !password) {
+ return res
+ .status(400)
+ .json({ error: "Email and password are required" });
+ }
+ const user = await User.findOne({ where: { email } });
+ if (!user || !user.password) {
+ return res.status(401).json({ error: "Invalid credentials" });
+ }
+ if (!(await comparePassword(password, user.password))) {
+ return res.status(401).json({ error: "Invalid credentials" });
+ }
+ const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET);
+ res.json({ token });
+ } catch (err) {
+ next(err);
+ }
+ },
+
+ async googleLogin(req, res, next) {
+ try {
+ const { idToken } = req.body;
+ if (!idToken) return res.status(400).json({ error: "Missing idToken" });
+
+ // Verifikasi token Google
+ const ticket = await client.verifyIdToken({
+ idToken,
+ audience: process.env.GOOGLE_CLIENT_ID,
+ });
+ const payload = ticket.getPayload();
+ const email = payload.email;
+
+ // Cari atau buat user di database
+ let user = await User.findOne({ where: { email } });
+ if (!user) {
+ const randomPassword = await hashPassword(
+ Math.random().toString(36).slice(-8)
+ ); // <-- perbaikan di sini
+ user = await User.create({
+ email,
+ password: randomPassword, // <-- gunakan hash random, bukan ""
+ name: payload.name || "",
+ });
+ }
+
+ // Generate JWT
+ const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET);
+
+ res.json({ token });
+ } catch (err) {
+ next(err);
+ }
+ },
+
+ async profile(req, res, next) {
+ try {
+ const user = await User.findByPk(req.user.id, {
+ attributes: ["id", "email", "name"],
+ });
+ if (!user) {
+ return res.status(404).json({ error: "User not found" });
+ }
+ res.json(user);
+ } catch (err) {
+ next(err);
+ }
+ },
+};
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
|---|---|---|---|---|---|---|---|---|---|
| authController.js | +
+
+ |
+ 35.29% | +18/51 | +14.81% | +4/27 | +75% | +3/4 | +36% | +18/50 | +
| marketController.js | +
+
+ |
+ 65.51% | +19/29 | +57.14% | +4/7 | +80% | +4/5 | +66.66% | +18/27 | +
| mydigimonController.js | +
+
+ |
+ 72.91% | +35/48 | +50% | +4/8 | +100% | +6/6 | +70.45% | +31/44 | +
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 | 1x +1x +1x +1x + +1x +1x +1x + + +209x + + + + + +1x + + + + + +1x +1x +1x + +1x +1x + + + + + + + + + + + + + + + + + + + + + + + +1x +1x +1x + + + + + + + + + + + + + +1x + + + | const axios = require("axios");
+const { MyDigimon } = require("../models");
+const { mapLevelToAttribute } = require("../helpers/mapLevelToAttribute");
+const { getGeminiRecommendation } = require("../helpers/gemini.helper");
+
+exports.getMarketDigimons = async (req, res, next) => {
+ try {
+ const { data } = await axios.get(
+ "https://digimon-api.vercel.app/api/digimon"
+ );
+ const digimons = data.map((d) => ({
+ name: d.name,
+ img: d.img,
+ level: d.level,
+ attribute: d.attribute || mapLevelToAttribute(d.level),
+ }));
+ res.json(digimons);
+ } catch (err) {
+ next(err);
+ }
+};
+
+exports.buyDigimon = async (req, res, next) => {
+ try {
+ const { digimonName, img, level } = req.body;
+
+ Eif (!digimonName || !img || !level) {
+ return res
+ .status(400)
+ .json({ error: "digimonName/img/level is required" });
+ }
+
+ const attribute = mapLevelToAttribute(level);
+
+ const newDigimon = await MyDigimon.create({
+ digimonName,
+ img,
+ level,
+ attribute,
+ power: 50,
+ hunger: 50,
+ happiness: 50,
+ userId: req.user.id,
+ });
+
+ res.status(201).json(newDigimon);
+ } catch (err) {
+ next(err);
+ }
+};
+
+exports.recommendation = async (req, res, next) => {
+ try {
+ const myDigimons = await MyDigimon.findAll({
+ where: { userId: req.user.id },
+ attributes: ["digimonName", "attribute"],
+ });
+
+ const attributes = [...new Set(myDigimons.map((d) => d.attribute))];
+ const prompt = `Saya punya Digimon dengan attribute: ${attributes.join(
+ ", "
+ )}. Rekomendasikan Digimon yang cocok untuk saya beli berikutnya.`;
+
+ const recommendation = await getGeminiRecommendation(prompt);
+
+ res.json({ recommendation });
+ } catch (err) {
+ next(err);
+ }
+};
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 | 1x +1x + +1x + + +1x +1x + + + + + + + + + + + + + + + + +1x + + + + + +1x +1x +1x + +1x + + + + + + + + + + + + +1x + + + + + +1x +1x +1x + + +1x + + + + + +1x + + + + + +1x +1x +1x + + +1x + + + + + +1x + + + + + +1x +1x +1x + + +1x + + + + + +1x + + + + + +1x +1x +1x + + +1x + + + + +1x + + + + | const { MyDigimon } = require("../models");
+const { mapLevelToAttribute } = require("../helpers/mapLevelToAttribute");
+
+module.exports = {
+ // GET all MyDigimons
+ async getAll(req, res, next) {
+ try {
+ const digimons = await MyDigimon.findAll({
+ where: { userId: req.user.id },
+ order: [["id", "ASC"]],
+ attributes: [
+ "id",
+ "digimonName",
+ "img",
+ "level",
+ "attribute",
+ "power",
+ "hunger",
+ "happiness",
+ "userId",
+ ],
+ });
+ res.json(digimons);
+ } catch (err) {
+ next(err);
+ }
+ },
+
+ // POST (add Digimon to user's collection)
+ async create(req, res, next) {
+ try {
+ const { digimonName, img, level } = req.body;
+ const attribute = mapLevelToAttribute(level);
+
+ const newDigimon = await MyDigimon.create({
+ userId: req.user.id,
+ digimonName,
+ img,
+ level,
+ attribute,
+ hunger: 50,
+ power: 50,
+ happiness: 50,
+ });
+
+ res.status(201).json(newDigimon);
+ } catch (err) {
+ next(err);
+ }
+ },
+
+ // PATCH - feed
+ async feed(req, res, next) {
+ try {
+ const { id } = req.params;
+ const digimon = await MyDigimon.findOne({
+ where: { id, userId: req.user.id },
+ });
+ Eif (!digimon) throw new Error("Digimon not found");
+
+ digimon.hunger += 1;
+ await digimon.save();
+ res.json(digimon);
+ } catch (err) {
+ next(err);
+ }
+ },
+
+ // PATCH - train
+ async train(req, res, next) {
+ try {
+ const { id } = req.params;
+ const digimon = await MyDigimon.findOne({
+ where: { id, userId: req.user.id },
+ });
+ Eif (!digimon) throw new Error("Digimon not found");
+
+ digimon.power += 1;
+ await digimon.save();
+ res.json(digimon);
+ } catch (err) {
+ next(err);
+ }
+ },
+
+ // PATCH - play
+ async play(req, res, next) {
+ try {
+ const { id } = req.params;
+ const digimon = await MyDigimon.findOne({
+ where: { id, userId: req.user.id },
+ });
+ Eif (!digimon) throw new Error("Digimon not found");
+
+ digimon.happiness += 1;
+ await digimon.save();
+ res.json(digimon);
+ } catch (err) {
+ next(err);
+ }
+ },
+
+ // DELETE
+ async destroy(req, res, next) {
+ try {
+ const { id } = req.params;
+ const digimon = await MyDigimon.findOne({
+ where: { id, userId: req.user.id },
+ });
+ Eif (!digimon) throw new Error("Digimon not found");
+
+ await digimon.destroy();
+ res.json({ message: "Digimon deleted successfully" });
+ } catch (err) {
+ next(err);
+ }
+ },
+};
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 | + + +1x +1x + + + + + + + + + + +1x + + + + + + + +1x + | 'use strict';
+const {
+ Model
+} = require('sequelize');
+module.exports = (sequelize, DataTypes) => {
+ class Digimon extends Model {
+ /**
+ * Helper method for defining associations.
+ * This method is not a part of Sequelize lifecycle.
+ * The `models/index` file will call this method automatically.
+ */
+ static associate(models) {
+ // define association here
+ }
+ }
+ Digimon.init({
+ name: DataTypes.STRING,
+ img: DataTypes.STRING,
+ level: DataTypes.STRING
+ }, {
+ sequelize,
+ modelName: 'Digimon',
+ });
+ return Digimon;
+}; |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 | 1x + + + +1x +1x +1x + + + + + + + + + + + + + + +1x + + + + + + + + +1x + | const axios = require("axios");
+
+async function getGeminiRecommendation(prompt) {
+ const url =
+ "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent";
+ try {
+ const response = await axios.post(
+ url,
+ {
+ contents: [
+ {
+ parts: [{ text: prompt }],
+ },
+ ],
+ },
+ {
+ headers: { "Content-Type": "application/json" },
+ params: { key: process.env.GEMINI_API_KEY },
+ }
+ );
+ // Ambil hasil rekomendasi dari response Gemini
+ return (
+ response.data.candidates?.[0]?.content?.parts?.[0]?.text ||
+ "Tidak ada rekomendasi."
+ );
+ } catch (err) {
+ throw new Error("Gagal mendapatkan rekomendasi dari Gemini");
+ }
+}
+
+module.exports = { getGeminiRecommendation };
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 | 1x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +1x + | const axios = require("axios");
+
+async function getGeminiRecommendation(prompt) {
+ const url =
+ "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent";
+ try {
+ const response = await axios.post(
+ url,
+ {
+ contents: [
+ {
+ parts: [{ text: prompt }],
+ },
+ ],
+ },
+ {
+ headers: { "Content-Type": "application/json" },
+ params: { key: process.env.GEMINI_API_KEY },
+ }
+ );
+ // Ambil hasil rekomendasi dari response Gemini
+ return (
+ response.data.candidates?.[0]?.content?.parts?.[0]?.text ||
+ "Tidak ada rekomendasi."
+ );
+ } catch (err) {
+ throw new Error("Gagal mendapatkan rekomendasi dari Gemini");
+ }
+}
+
+module.exports = { getGeminiRecommendation };
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
|---|---|---|---|---|---|---|---|---|---|
| gemini.helper.js | +
+
+ |
+ 28.57% | +2/7 | +0% | +0/2 | +0% | +0/1 | +28.57% | +2/7 | +
| jwt.helper.js | +
+
+ |
+ 50% | +2/4 | +100% | +0/0 | +0% | +0/2 | +50% | +2/4 | +
| mapLevelToAttribute.js | +
+
+ |
+ 100% | +3/3 | +100% | +2/2 | +100% | +1/1 | +100% | +3/3 | +
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 | 1x + + + + + + + + + +1x + | const bcrypt = require("bcryptjs");
+
+function hashPassword(password) {
+ return bcrypt.hash(password, 10);
+}
+
+function comparePassword(password, hash) {
+ return bcrypt.compare(password, hash);
+}
+
+module.exports = { hashPassword, comparePassword };
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 | 1x + + + + + + + + + + +210x + + +1x + | const levelToAttribute = {
+ Fresh: "Baby",
+ "In Training": "Data",
+ Rookie: "Vaccine",
+ Champion: "Data",
+ Ultimate: "Virus",
+ Mega: "Free",
+
+};
+
+function mapLevelToAttribute(level) {
+ return levelToAttribute[level] || "Armor";
+}
+
+module.exports = { mapLevelToAttribute };
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
|---|---|---|---|---|---|---|---|---|---|
| server | +
+
+ |
+ 100% | +15/15 | +100% | +0/0 | +100% | +0/0 | +100% | +15/15 | +
| server/controllers | +
+
+ |
+ 87.5% | +112/128 | +85.71% | +36/42 | +93.33% | +14/15 | +87.6% | +106/121 | +
| server/helpers | +
+
+ |
+ 92.85% | +13/14 | +100% | +4/4 | +100% | +4/4 | +92.85% | +13/14 | +
| server/middlewares | +
+
+ |
+ 76.19% | +16/21 | +66.66% | +8/12 | +100% | +2/2 | +76.19% | +16/21 | +
| server/models | +
+
+ |
+ 97.14% | +34/35 | +70% | +7/10 | +100% | +9/9 | +97.14% | +34/35 | +
| server/routes | +
+
+ |
+ 100% | +28/28 | +100% | +0/0 | +100% | +0/0 | +100% | +28/28 | +
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 | + +1x +1x +1x +1x +1x +1x +1x +1x + + +1x + + +1x + + +1x + + +4x + + + + + + + +3x +3x + + +1x +3x +3x + + + +1x +1x + +1x + | 'use strict';
+
+const fs = require('fs');
+const path = require('path');
+const Sequelize = require('sequelize');
+const process = require('process');
+const basename = path.basename(__filename);
+const env = process.env.NODE_ENV || 'development';
+const config = require(__dirname + '/../config/config.json')[env];
+const db = {};
+
+let sequelize;
+Iif (config.use_env_variable) {
+ sequelize = new Sequelize(process.env[config.use_env_variable], config);
+} else {
+ sequelize = new Sequelize(config.database, config.username, config.password, config);
+}
+
+fs
+ .readdirSync(__dirname)
+ .filter(file => {
+ return (
+ file.indexOf('.') !== 0 &&
+ file !== basename &&
+ file.slice(-3) === '.js' &&
+ file.indexOf('.test.js') === -1
+ );
+ })
+ .forEach(file => {
+ const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
+ db[model.name] = model;
+ });
+
+Object.keys(db).forEach(modelName => {
+ Eif (db[modelName].associate) {
+ db[modelName].associate(db);
+ }
+});
+
+db.sequelize = sequelize;
+db.Sequelize = Sequelize;
+
+module.exports = db;
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 | + + +1x +1x + + + + + + + + + + +1x + + + + + + + +1x + | 'use strict';
+const {
+ Model
+} = require('sequelize');
+module.exports = (sequelize, DataTypes) => {
+ class Digimon extends Model {
+ /**
+ * Helper method for defining associations.
+ * This method is not a part of Sequelize lifecycle.
+ * The `models/index` file will call this method automatically.
+ */
+ static associate(models) {
+ // define association here
+ }
+ }
+ Digimon.init({
+ name: DataTypes.STRING,
+ img: DataTypes.STRING,
+ level: DataTypes.STRING
+ }, {
+ sequelize,
+ modelName: 'Digimon',
+ });
+ return Digimon;
+}; |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
|---|---|---|---|---|---|---|---|---|---|
| digimon.js | +
+
+ |
+ 100% | +4/4 | +100% | +0/0 | +100% | +2/2 | +100% | +4/4 | +
| index.js | +
+
+ |
+ 95.23% | +20/21 | +70% | +7/10 | +100% | +3/3 | +95.23% | +20/21 | +
| mydigimon.js | +
+
+ |
+ 100% | +5/5 | +100% | +0/0 | +100% | +2/2 | +100% | +5/5 | +
| user.js | +
+
+ |
+ 100% | +5/5 | +100% | +0/0 | +100% | +2/2 | +100% | +5/5 | +
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 | + +1x +1x +1x +1x +1x +1x +1x +1x + + +1x + + +1x + + +1x + + +4x + + + + + + + +3x +3x + + +1x +3x +3x + + + +1x +1x + +1x + | 'use strict';
+
+const fs = require('fs');
+const path = require('path');
+const Sequelize = require('sequelize');
+const process = require('process');
+const basename = path.basename(__filename);
+const env = process.env.NODE_ENV || 'development';
+const config = require(__dirname + '/../config/config.json')[env];
+const db = {};
+
+let sequelize;
+Iif (config.use_env_variable) {
+ sequelize = new Sequelize(process.env[config.use_env_variable], config);
+} else {
+ sequelize = new Sequelize(config.database, config.username, config.password, config);
+}
+
+fs
+ .readdirSync(__dirname)
+ .filter(file => {
+ return (
+ file.indexOf('.') !== 0 &&
+ file !== basename &&
+ file.slice(-3) === '.js' &&
+ file.indexOf('.test.js') === -1
+ );
+ })
+ .forEach(file => {
+ const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
+ db[model.name] = model;
+ });
+
+Object.keys(db).forEach(modelName => {
+ Eif (db[modelName].associate) {
+ db[modelName].associate(db);
+ }
+});
+
+db.sequelize = sequelize;
+db.Sequelize = Sequelize;
+
+module.exports = db;
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 | + + +1x +1x + + + + + + + +1x + + +1x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +1x + | 'use strict';
+const {
+ Model
+} = require('sequelize');
+module.exports = (sequelize, DataTypes) => {
+ class MyDigimon extends Model {
+ /**
+ * Helper method for defining associations.
+ * This method is not a part of Sequelize lifecycle.
+ * The `models/index` file will call this method automatically.
+ */
+ static associate(models) {
+ MyDigimon.belongsTo(models.User, { foreignKey: "userId" });
+ }
+ }
+ MyDigimon.init(
+ {
+ userId: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ },
+ digimonName: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ },
+ img: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ },
+ level: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ },
+ attribute: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ },
+ hunger: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ defaultValue: 50,
+ },
+ power: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ defaultValue: 50,
+ },
+ happiness: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ defaultValue: 50,
+ },
+ },
+ {
+ sequelize,
+ modelName: "MyDigimon",
+ }
+ );
+ return MyDigimon;
+}; |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 | +1x +1x + + +1x + + +1x + + + + + + + + + + + + + + + + + +1x + + | "use strict";
+const { Model } = require("sequelize");
+module.exports = (sequelize, DataTypes) => {
+ class User extends Model {
+ static associate(models) {
+ User.hasMany(models.MyDigimon, { foreignKey: "userId" });
+ }
+ }
+ User.init(
+ {
+ email: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ unique: true,
+ validate: { isEmail: true },
+ },
+ password: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ },
+ },
+ {
+ sequelize,
+ modelName: "User",
+ }
+ );
+ return User;
+};
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 | + + +1x +1x + + + + + + + +1x + + +1x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +1x + | 'use strict';
+const {
+ Model
+} = require('sequelize');
+module.exports = (sequelize, DataTypes) => {
+ class MyDigimon extends Model {
+ /**
+ * Helper method for defining associations.
+ * This method is not a part of Sequelize lifecycle.
+ * The `models/index` file will call this method automatically.
+ */
+ static associate(models) {
+ MyDigimon.belongsTo(models.User, { foreignKey: "userId" });
+ }
+ }
+ MyDigimon.init(
+ {
+ userId: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ },
+ digimonName: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ },
+ img: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ },
+ level: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ },
+ attribute: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ },
+ hunger: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ defaultValue: 50,
+ },
+ power: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ defaultValue: 50,
+ },
+ happiness: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ defaultValue: 50,
+ },
+ },
+ {
+ sequelize,
+ modelName: "MyDigimon",
+ }
+ );
+ return MyDigimon;
+}; |