diff --git a/backend/controllers/falukantController.js b/backend/controllers/falukantController.js index 90ca564..865052a 100644 --- a/backend/controllers/falukantController.js +++ b/backend/controllers/falukantController.js @@ -1,13 +1,69 @@ -import * as falukantService from '../services/falukantService.js'; +import FalukantService from '../services/falukantService.js'; class FalukantController { constructor() { - this.exampleMethod = this.exampleMethod.bind(this); + this.getUser = this.getUser.bind(this); + this.createUser = this.createUser.bind(this); + this.randomFirstName = this.randomFirstName.bind(this); + this.randomLastName = this.randomLastName.bind(this); + this.getInfo = this.getInfo.bind(this); } - async exampleMethod(req, res) { + async getUser(req, res) { try { - const result = await falukantService.exampleMethod(); + const { userid: hashedUserId } = req.headers; + const result = await FalukantService.getUser(hashedUserId); + res.status(200).json(result); + } catch (error) { + res.status(500).json({ error: error.message }); + } + } + + async createUser(req, res) { + try { + const { userid: hashedUserId } = req.headers; + const { gender, firstname: firstName, lastname: lastName } = req.body; + console.log(req.body); + const result = await FalukantService.createUser(hashedUserId, gender, firstName, lastName); + res.status(201).json(result); + } catch (error) { + res.status(500).json({ error: error.message }); + } + } + + async randomFirstName(req, res) { + try { + const { gender } = req.params; + const result = await FalukantService.randomFirstName(gender); + res.status(200).json({ name: result }); + } catch (error) { + res.status(500).json({ error: error.message }); + } + } + + async randomLastName(req, res) { + try { + const result = await FalukantService.randomLastName(); + res.status(200).json({ name: result }); + } catch (error) { + res.status(500).json({ error: error.message }); + } + } + + async getInfo(req, res) { + try { + const { userid: hashedUserId } = req.headers; + const result = await FalukantService.getInfo(hashedUserId); + res.status(200).json(result); + } catch (error) { + res.status(500).json({ error: error.message }); + } + } + + async getBranches(req, res) { + try { + const { userid: hashedUserId } = req.headers; + const result = await FalukantService.getBranches(hashedUserId); res.status(200).json(result); } catch (error) { res.status(500).json({ error: error.message }); diff --git a/backend/controllers/navigationController.js b/backend/controllers/navigationController.js index 5b0b919..e69ca88 100644 --- a/backend/controllers/navigationController.js +++ b/backend/controllers/navigationController.js @@ -87,7 +87,7 @@ const menuStructure = { }, towns: { visible: ["hasfalukantaccount"], - path: "/falukant/towns" + path: "/falukant/branch" }, directors: { visible: ["hasfalukantaccount"], @@ -117,6 +117,10 @@ const menuStructure = { visible: ["hasfalukantaccount"], path: "/falukant/education" }, + health: { + visible: ["hasfalukantaccount"], + path: "/falukant/health" + }, bank: { visible: ["hasfalukantaccount"], path: "/falukant/bank" @@ -238,7 +242,6 @@ class NavigationController { } async filterMenu(menu, rights, age, userId) { - console.log(userId); const filteredMenu = {}; const hasFalukantAccount = await this.hasFalukantAccount(userId); for (const [key, value] of Object.entries(menu)) { diff --git a/backend/models/associations.js b/backend/models/associations.js index 273c26d..c5caf0d 100644 --- a/backend/models/associations.js +++ b/backend/models/associations.js @@ -31,6 +31,18 @@ import Friendship from './community/friendship.js'; import FalukantUser from './falukant/data/user.js'; import RegionType from './falukant/type/region.js'; import RegionData from './falukant/data/region.js'; +import FalukantCharacter from './falukant/data/character.js'; +import FalukantPredefineFirstname from './falukant/predefine/firstname.js'; +import FalukantPredefineLastname from './falukant/predefine/lastname.js'; +import FalukantStock from './falukant/data/stock.js'; +import FalukantStockType from './falukant/type/stock.js'; +import Knowledge from './falukant/data/product_knowledge.js'; +import ProductType from './falukant/type/product.js'; +import TitleOfNobility from './falukant/type/title_of_nobility.js'; +import TitleRequirement from './falukant/type/title_requirement.js'; +import Branch from './falukant/data/branch.js'; +import BranchType from './falukant/type/branch.js'; + export default function setupAssociations() { // UserParam related associations @@ -181,4 +193,46 @@ export default function setupAssociations() { FalukantUser.belongsTo(RegionData, { foreignKey: 'mainBranchRegionId', as: 'mainBranchRegion' }); RegionData.hasMany(FalukantUser, { foreignKey: 'mainBranchRegionId', as: 'users' }); + + FalukantCharacter.belongsTo(FalukantUser, { foreignKey: 'userId', as: 'user' }); + FalukantUser.hasOne(FalukantCharacter, { foreignKey: 'userId', as: 'character' }); + + FalukantCharacter.belongsTo(FalukantPredefineFirstname, { foreignKey: 'firstName', as: 'definedFirstName' }); + FalukantPredefineFirstname.hasMany(FalukantCharacter, { foreignKey: 'firstName', as: 'charactersWithFirstName' }); + + FalukantCharacter.belongsTo(FalukantPredefineLastname, { foreignKey: 'lastName', as: 'definedLastName' }); + FalukantPredefineLastname.hasMany(FalukantCharacter, { foreignKey: 'lastName', as: 'charactersWithLastName' }); + + FalukantCharacter.belongsTo(TitleOfNobility, { foreignKey: 'titleOfNobility', as: 'nobleTitle' }); + TitleOfNobility.hasMany(FalukantCharacter, { foreignKey: 'titleOfNobility', as: 'charactersWithNobleTitle' }); + + FalukantCharacter.belongsTo(RegionData, { foreignKey: 'regionId', as: 'region' }); + RegionData.hasMany(FalukantCharacter, { foreignKey: 'regionId', as: 'charactersInRegion' }); + + FalukantStock.belongsTo(FalukantStockType, { foreignKey: 'stockTypeId', as: 'stockType' }); + FalukantStockType.hasMany(FalukantStock, { foreignKey: 'stockTypeId', as: 'stocks' }); + + FalukantStock.belongsTo(FalukantUser, { foreignKey: 'userId', as: 'user' }); + FalukantUser.hasMany(FalukantStock, { foreignKey: 'userId', as: 'stocks' }); + + FalukantStock.belongsTo(RegionData, { foreignKey: 'regionId', as: 'region' }); + RegionData.hasMany(FalukantStock, { foreignKey: 'regionId', as: 'stocksInRegion' }); + + Knowledge.belongsTo(ProductType, { foreignKey: 'productTypeId', as: 'productType' }); + ProductType.hasMany(Knowledge, { foreignKey: 'productTypeId', as: 'knowledges' }); + + Knowledge.belongsTo(FalukantCharacter, { foreignKey: 'characterId', as: 'character' }); + FalukantCharacter.hasMany(Knowledge, { foreignKey: 'characterId', as: 'knowledges' }); + + TitleRequirement.belongsTo(TitleOfNobility, { foreignKey: 'titleId', as: 'title' }); + TitleOfNobility.hasMany(TitleRequirement, { foreignKey: 'titleId', as: 'requirements' }); + + Branch.belongsTo(RegionData, { foreignKey: 'regionId', as: 'region' }); + RegionData.hasMany(Branch, { foreignKey: 'regionId', as: 'branches' }); + + Branch.belongsTo(FalukantUser, { foreignKey: 'falukantUserId', as: 'user' }); + FalukantUser.hasMany(Branch, { foreignKey: 'falukantUserId', as: 'branches' }); + + Branch.belongsTo(BranchType, { foreignKey: 'branchTypeId', as: 'branchType' }); + BranchType.hasMany(Branch, { foreignKey: 'branchTypeId', as: 'branches' }); } diff --git a/backend/models/falukant/data/branch.js b/backend/models/falukant/data/branch.js new file mode 100644 index 0000000..3f1ccad --- /dev/null +++ b/backend/models/falukant/data/branch.js @@ -0,0 +1,34 @@ +import { Model, DataTypes } from 'sequelize'; +import { sequelize } from '../../../utils/sequelize.js'; + +class Branch extends Model { } + +Branch.init({ + branchTypeId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + regionId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + falukantUserId: { + type: DataTypes.INTEGER, + allowNull: false, + }, +}, { + sequelize, + modelName: 'BranchType', + tableName: 'branch', + schema: 'falukant_data', + timestamps: false, + underscored: true, + indexes: [ + { + unique: true, + fields: ['region_id', 'falukant_user_id'] + } + ], +}); + +export default Branch; diff --git a/backend/models/falukant/data/character.js b/backend/models/falukant/data/character.js new file mode 100644 index 0000000..450199b --- /dev/null +++ b/backend/models/falukant/data/character.js @@ -0,0 +1,49 @@ +import { Model, DataTypes } from 'sequelize'; +import { sequelize } from '../../../utils/sequelize.js'; + +class FalukantCharacter extends Model {}; + +FalukantCharacter.init({ + userId: { + type: DataTypes.INTEGER, + allowNull: true, + }, + regionId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + firstName: { + type: DataTypes.INTEGER, + allowNull: false, + }, + lastName: { + type: DataTypes.INTEGER, + allowNull: false, + }, + birthdate: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW + }, + gender: { + type: DataTypes.STRING + }, + health: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 100 + }, + titleOfNobility: { + type: DataTypes.INTEGER, + allowNull: false, + } +}, { + sequelize, + modelName: 'FalukantCharacter', + tableName: 'character', + schema: 'falukant_data', + timestamps: true, + underscored: true, +}); + +export default FalukantCharacter; diff --git a/backend/models/falukant/data/product_knowledge.js b/backend/models/falukant/data/product_knowledge.js new file mode 100644 index 0000000..b40066f --- /dev/null +++ b/backend/models/falukant/data/product_knowledge.js @@ -0,0 +1,34 @@ +import { Model, DataTypes } from 'sequelize'; +import { sequelize } from '../../../utils/sequelize.js'; + +class Knowledge extends Model { } + +Knowledge.init({ + productId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + characterId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + knowledge: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 0, + } +}, { + sequelize, + modelName: 'Knowledge', + tableName: 'knowledge', + schema: 'falukant_data', + timestamps: false, + underscored: true, + hooks: { + beforeCreate: (knowledge) => { + knowledge.knowledge = Math.floor(Math.random() * 61) + 20; + } + } +}); + +export default Knowledge; diff --git a/backend/models/falukant/data/stock.js b/backend/models/falukant/data/stock.js new file mode 100644 index 0000000..9ac8ffc --- /dev/null +++ b/backend/models/falukant/data/stock.js @@ -0,0 +1,32 @@ +import { Model, DataTypes } from 'sequelize'; +import { sequelize } from '../../../utils/sequelize.js'; + +class FalukantStock extends Model { } + +FalukantStock.init({ + userId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + regionId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + stockTypeId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + quantity: { + type: DataTypes.INTEGER, + allowNull: false, + }, +}, { + sequelize, + modelName: 'StockType', + tableName: 'stock', + schema: 'falukant_data', + timestamps: false, + underscored: true, +}); + +export default FalukantStock; diff --git a/backend/models/falukant/data/user.js b/backend/models/falukant/data/user.js index 4ed95d4..d568b16 100644 --- a/backend/models/falukant/data/user.js +++ b/backend/models/falukant/data/user.js @@ -14,7 +14,8 @@ FalukantUser.init({ schema: 'community' }, key: 'id' - } + }, + unique: true, }, money: { type: DataTypes.DECIMAL(10, 2), diff --git a/backend/models/falukant/predefine/firstname.js b/backend/models/falukant/predefine/firstname.js new file mode 100644 index 0000000..65561d5 --- /dev/null +++ b/backend/models/falukant/predefine/firstname.js @@ -0,0 +1,26 @@ +import { sequelize } from '../../../utils/sequelize.js'; +import { DataTypes } from 'sequelize'; + +const FalukantPredefineFirstname = sequelize.define('firstname', { + name: { + type: DataTypes.STRING, + allowNull: false + }, + gender: { + type: DataTypes.STRING, + allowNull: false + } +}, { + tableName: 'firstname', + schema: 'falukant_predefine', + underscored: true, + timestamps: false, + indexes: [ + { + unique: true, + fields: ['name', 'gender'] + } + ], +}); + +export default FalukantPredefineFirstname; \ No newline at end of file diff --git a/backend/models/falukant/predefine/lastname.js b/backend/models/falukant/predefine/lastname.js new file mode 100644 index 0000000..3c4a49d --- /dev/null +++ b/backend/models/falukant/predefine/lastname.js @@ -0,0 +1,23 @@ +import { sequelize } from '../../../utils/sequelize.js'; +import { DataTypes } from 'sequelize'; + +const FalukantPredefineLastname = sequelize.define('lastname', { + name: { + type: DataTypes.STRING, + length: 1, + allowNull: false + }, +}, { + tableName: 'lastname', + schema: 'falukant_predefine', + underscored: true, + timestamps: false, + indexes: [ + { + unique: true, + fields: ['name'] + } + ], +}); + +export default FalukantPredefineLastname; \ No newline at end of file diff --git a/backend/models/falukant/type/branch.js b/backend/models/falukant/type/branch.js new file mode 100644 index 0000000..a9a5b93 --- /dev/null +++ b/backend/models/falukant/type/branch.js @@ -0,0 +1,30 @@ +import { Model, DataTypes } from 'sequelize'; +import { sequelize } from '../../../utils/sequelize.js'; + +class BranchType extends Model { } + +BranchType.init({ + labelTr: { + type: DataTypes.STRING, + allowNull: false, + }, + baseCost: { + type: DataTypes.INTEGER, + allowNull: false, + } +}, { + sequelize, + modelName: 'BranchType', + tableName: 'branch', + schema: 'falukant_type', + timestamps: false, + underscored: true, + indexes: [ + { + unique: true, + fields: ['label_tr'] + } + ], +}); + +export default BranchType; diff --git a/backend/models/falukant/type/product.js b/backend/models/falukant/type/product.js new file mode 100644 index 0000000..d4a096c --- /dev/null +++ b/backend/models/falukant/type/product.js @@ -0,0 +1,38 @@ +import { Model, DataTypes } from 'sequelize'; +import { sequelize } from '../../../utils/sequelize.js'; + +class ProductType extends Model { } + +ProductType.init({ + labelTr: { + type: DataTypes.STRING, + allowNull: false, + }, + category: { + type: DataTypes.INTEGER, + allowNull: false, + }, + productionTime: { + type: DataTypes.INTEGER, + allowNull: false, + }, + sellCost: { + type: DataTypes.INTEGER, + allowNull: false, + } +}, { + sequelize, + modelName: 'ProductType', + tableName: 'product', + schema: 'falukant_type', + timestamps: false, + underscored: true, + indexes: [ + { + unique: true, + fields: ['label_tr'] + } + ], +}); + +export default ProductType; diff --git a/backend/models/falukant/type/stock.js b/backend/models/falukant/type/stock.js new file mode 100644 index 0000000..1fde918 --- /dev/null +++ b/backend/models/falukant/type/stock.js @@ -0,0 +1,24 @@ +import { Model, DataTypes } from 'sequelize'; +import { sequelize } from '../../../utils/sequelize.js'; + +class FalukantStockType extends Model { } + +FalukantStockType.init({ + labelTr: { + type: DataTypes.STRING, + allowNull: false, + }, + cost: { + type: DataTypes.INTEGER, + allowNull: false, + } +}, { + sequelize, + modelName: 'StockType', + tableName: 'stock', + schema: 'falukant_type', + timestamps: false, + underscored: true, +}); + +export default FalukantStockType; diff --git a/backend/models/falukant/type/title_of_nobility.js b/backend/models/falukant/type/title_of_nobility.js new file mode 100644 index 0000000..5b70de7 --- /dev/null +++ b/backend/models/falukant/type/title_of_nobility.js @@ -0,0 +1,20 @@ +import { Model, DataTypes } from 'sequelize'; +import { sequelize } from '../../../utils/sequelize.js'; + +class TitleOfNobility extends Model { } + +TitleOfNobility.init({ + labelTr: { + type: DataTypes.STRING, + allowNull: false, + }, +}, { + sequelize, + modelName: 'Title', + tableName: 'title', + schema: 'falukant_type', + timestamps: false, + underscored: true, +}); + +export default TitleOfNobility; diff --git a/backend/models/falukant/type/title_requirement.js b/backend/models/falukant/type/title_requirement.js new file mode 100644 index 0000000..2996252 --- /dev/null +++ b/backend/models/falukant/type/title_requirement.js @@ -0,0 +1,30 @@ +import { Model, DataTypes } from 'sequelize'; +import { sequelize } from '../../../utils/sequelize.js'; + +class TitleRequirement extends Model { } + +TitleRequirement.init({ + titleId: { + type: DataTypes.INTEGER, + allowNull: false, + primaryKey: true, + autoIncrement: true, + }, + requirementType: { + type: DataTypes.STRING, + allowNull: false, + }, + requirementValue: { + type: DataTypes.DECIMAL(14, 2), + allowNull: false, + }, +}, { + sequelize, + modelName: 'TitleRequirement', + tableName: 'title_requirement', + schema: 'falukant_type', + timestamps: false, + underscored: true, +}); + +export default TitleRequirement; diff --git a/backend/models/index.js b/backend/models/index.js index ada7b08..070bd81 100644 --- a/backend/models/index.js +++ b/backend/models/index.js @@ -35,6 +35,17 @@ import Friendship from './community/friendship.js'; import FalukantUser from './falukant/data/user.js'; import RegionType from './falukant/type/region.js'; import RegionData from './falukant/data/region.js'; +import FalukantPredefineFirstname from './falukant/predefine/firstname.js'; +import FalukantPredefineLastname from './falukant/predefine/lastname.js'; +import FalukantCharacter from './falukant/data/character.js'; +import FalukantStock from './falukant/data/stock.js'; +import FalukantStockType from './falukant/type/stock.js'; +import ProductType from './falukant/type/product.js'; +import Knowledge from './falukant/data/product_knowledge.js'; +import TitleRequirement from './falukant/type/title_requirement.js'; +import TitleOfNobility from './falukant/type/title_of_nobility.js'; +import BranchType from './falukant/type/branch.js'; +import Branch from './falukant/data/branch.js'; const models = { SettingsType, @@ -74,6 +85,17 @@ const models = { RegionType, RegionData, FalukantUser, + FalukantPredefineFirstname, + FalukantPredefineLastname, + FalukantCharacter, + FalukantStock, + FalukantStockType, + ProductType, + Knowledge, + TitleOfNobility, + TitleRequirement, + BranchType, + Branch, }; export default models; diff --git a/backend/models/trigger.js b/backend/models/trigger.js index c551845..73b9da2 100644 --- a/backend/models/trigger.js +++ b/backend/models/trigger.js @@ -2,7 +2,7 @@ import { sequelize } from '../utils/sequelize.js'; export async function createTriggers() { const createTriggerFunction = ` - CREATE OR REPLACE FUNCTION create_user_param_visibility_trigger() + CREATE OR REPLACE FUNCTION community.create_user_param_visibility_trigger() RETURNS TRIGGER AS $$ BEGIN -- Check if UserParamVisibility already exists for this UserParam @@ -32,7 +32,7 @@ export async function createTriggers() { AFTER INSERT ON community.user_param FOR EACH ROW WHEN (NEW.id IS NOT NULL) - EXECUTE FUNCTION create_user_param_visibility_trigger(); + EXECUTE FUNCTION community.create_user_param_visibility_trigger(); `; const createUpdateTrigger = ` @@ -40,11 +40,11 @@ export async function createTriggers() { AFTER UPDATE ON community.user_param FOR EACH ROW WHEN (NEW.id IS NOT NULL) - EXECUTE FUNCTION create_user_param_visibility_trigger(); + EXECUTE FUNCTION community.create_user_param_visibility_trigger(); `; const createDiaryHistoryTriggerFunction = ` - CREATE OR REPLACE FUNCTION insert_diary_history() + CREATE OR REPLACE FUNCTION community.insert_diary_history() RETURNS TRIGGER AS $$ BEGIN INSERT INTO community.diary_history (diary_id, user_id, old_text, old_created_at, old_updated_at) @@ -65,11 +65,11 @@ export async function createTriggers() { BEFORE UPDATE ON community.diary FOR EACH ROW WHEN (OLD.id IS NOT NULL) - EXECUTE FUNCTION insert_diary_history(); + EXECUTE FUNCTION community.insert_diary_history(); `; const createTitleHistoryTriggerFunction = ` - CREATE OR REPLACE FUNCTION insert_title_history() + CREATE OR REPLACE FUNCTION forum.insert_title_history() RETURNS TRIGGER AS $$ BEGIN INSERT INTO forum.title_history (title_id, old_title, changed_by, old_updated_at) @@ -84,7 +84,44 @@ export async function createTriggers() { BEFORE UPDATE ON forum.title FOR EACH ROW WHEN (OLD.id IS NOT NULL) - EXECUTE FUNCTION insert_title_history(); + EXECUTE FUNCTION forum.insert_title_history(); + `; + + const createCharacterCreationTriggerMethod = ` + CREATE OR REPLACE FUNCTION falukant_data.create_character_creation_trigger() + RETURNS TRIGGER AS $$ + BEGIN + INSERT INTO falukant_data.knowledge (product_id, character_id) + SELECT id, NEW.id FROM falukant_type.product; + RETURN NEW; + END; + $$ LANGUAGE plpgsql; + `; + + const createCharacterCreationTrigger = ` + CREATE OR REPLACE TRIGGER character_creation_trigger + AFTER INSERT ON falukant_data.character + FOR EACH ROW + WHEN (NEW.id IS NOT NULL) + EXECUTE FUNCTION falukant_data.create_character_creation_trigger(); + `; + + const createKnowledgeTriggerMethod = ` + CREATE OR REPLACE FUNCTION falukant_data.create_knowledge_trigger() + RETURNS TRIGGER AS $$ + BEGIN + NEW.knowledge = random() * 61 + 20; + RETURN NEW; + END; + $$ LANGUAGE plpgsql; + `; + + const createKnowledgeTrigger = ` + CREATE OR REPLACE TRIGGER knowledge_trigger + BEFORE INSERT ON falukant_data.knowledge + FOR EACH ROW + WHEN (NEW.id IS NOT NULL) + EXECUTE FUNCTION falukant_data.create_knowledge_trigger(); `; try { @@ -95,10 +132,14 @@ export async function createTriggers() { await sequelize.query(createDiaryHistoryTrigger); await sequelize.query(createTitleHistoryTriggerFunction); await sequelize.query(createTitleHistoryTrigger); + await sequelize.query(createCharacterCreationTriggerMethod); + await sequelize.query(createCharacterCreationTrigger); + await sequelize.query(createKnowledgeTriggerMethod); + await sequelize.query(createKnowledgeTrigger); console.log('Triggers created successfully'); } catch (error) { console.error('Error creating triggers:', error); } - } + diff --git a/backend/package-lock.json b/backend/package-lock.json index 1aa9e91..4b4c835 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -13,6 +13,7 @@ "bcrypt": "^5.1.1", "connect-redis": "^7.1.1", "cors": "^2.8.5", + "date-fns": "^4.1.0", "dompurify": "^3.1.7", "dotenv": "^16.4.5", "express": "^4.19.2", @@ -1231,9 +1232,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -1311,6 +1312,15 @@ "node": ">=18" } }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/debug": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", diff --git a/backend/package.json b/backend/package.json index fec01b8..cc5793e 100644 --- a/backend/package.json +++ b/backend/package.json @@ -15,6 +15,7 @@ "bcrypt": "^5.1.1", "connect-redis": "^7.1.1", "cors": "^2.8.5", + "date-fns": "^4.1.0", "dompurify": "^3.1.7", "dotenv": "^16.4.5", "express": "^4.19.2", diff --git a/backend/routers/falukantRouter.js b/backend/routers/falukantRouter.js index 3c09543..20ee292 100644 --- a/backend/routers/falukantRouter.js +++ b/backend/routers/falukantRouter.js @@ -4,6 +4,10 @@ import FalukantController from '../controllers/falukantController.js'; const router = express.Router(); const falukantController = new FalukantController(); -router.get('/example', falukantController.exampleMethod); - +router.get('/user', falukantController.getUser); +router.post('/user', falukantController.createUser); +router.get('/name/randomfirstname/:gender', falukantController.randomFirstName); +router.get('/name/randomlastname', falukantController.randomLastName); +router.get('/info', falukantController.getInfo); +router.get('/branches', falukantController.getBranches); export default router; diff --git a/backend/services/falukantService.js b/backend/services/falukantService.js index c84c698..042df5c 100644 --- a/backend/services/falukantService.js +++ b/backend/services/falukantService.js @@ -1,7 +1,284 @@ -class FalukantService { - async exampleMethod() { - // Logik für die Methode +import BaseService from './BaseService.js'; +import FalukantPredefineFirstname from '../models/falukant/predefine/firstname.js'; +import FalukantPredefineLastname from '../models/falukant/predefine/lastname.js'; +import FalukantUser from '../models/falukant/data/user.js'; +import User from '../models/community/user.js'; +import FalukantCharacter from '../models/falukant/data/character.js'; +import RegionData from '../models/falukant/data/region.js'; +import RegionType from '../models/falukant/type/region.js'; +import { Sequelize } from 'sequelize'; +import FalukantStock from '../models/falukant/data/stock.js'; +import FalukantStockType from '../models/falukant/type/stock.js'; +import { notifyUser } from '../utils/socket.js'; +import { differenceInDays } from 'date-fns'; +import TitleOfNobility from '../models/falukant/type/title_of_nobility.js'; +import Branch from '../models/falukant/data/branch.js'; +import BranchType from '../models/falukant/type/branch.js'; + +class FalukantService extends BaseService { + async getFalukantUserByHashedId(hashedId) { + const falukantUser = await FalukantUser.findOne({ + include: [{ + model: User, + as: 'user', + attributes: ['username', 'hashedId'], + where: { + hashedId: hashedId + }, + } + ], + }); + return falukantUser; } + + async getUser(hashedUserId) { + const falukantUser = await FalukantUser.findOne({ + include: [{ + model: User, + as: 'user', + attributes: ['username', 'hashedId'], + where: { + hashedId: hashedUserId + }, + }, + { + model: FalukantCharacter, + as: 'character', + include: [ + { + model: FalukantPredefineFirstname, + as: 'definedFirstName', + attributes: ['name'] + }, + { + model: FalukantPredefineLastname, + as: 'definedLastName', + attributes: ['name'] + }, + { + model: TitleOfNobility, + as: 'nobleTitle', + attributes: ['labelTr'] + } + ], + attributes: ['birthdate', 'gender'], + }, + { + model: RegionData, + as: 'mainBranchRegion', + include: [ + { + model: RegionType, + as: 'regionType', + } + ], + attributes: ['name'], + }, + { + model: Branch, + as: 'branches', + include: [ + { + model: BranchType, + as: 'branchType', + attributes: ['labelTr'] + }, + { + model: RegionData, + as: 'region', + include: [ + { + model: RegionType, + as: 'regionType', + } + ], + attributes: ['name'], + } + ] + } + ], + attributes: ['money', 'creditAmount', 'todayCreditTaken'], + }); + if (!falukantUser) { + throw new Error('User not found'); + } + const character = falukantUser.character; + if (character && character.birthdate) { + const birthdate = new Date(character.birthdate); + birthdate.setHours(0); + birthdate.setMinutes(0); + const currentDate = new Date(); + currentDate.setHours(0); + currentDate.setMinutes(0); + const ageInDays = differenceInDays(currentDate, birthdate); + character.setDataValue('age', ageInDays); + } + return falukantUser; + } + + async randomFirstName(gender) { + const names = await FalukantPredefineFirstname.findAll({ where: { gender: gender } }); + return names[Math.floor(Math.random() * names.length)].name; + } + + async randomLastName() { + const names = await FalukantPredefineLastname.findAll(); + return names[Math.floor(Math.random() * names.length)].name; + } + + async createUser(hashedUserId, gender, firstName, lastName) { + try { + const user = await this.getUserByHashedId(hashedUserId); + const userExistsCheck = await FalukantUser.findOne({ where: { userId: user.id } }); + if (userExistsCheck) { + throw new Error('User already exists in Falukant.'); + } + let firstNameObject = await FalukantPredefineFirstname.findOne({ where: { name: firstName } }); + let lastNameObject = await FalukantPredefineLastname.findOne({ where: { name: lastName } }); + if (!firstNameObject) { + firstNameObject = await FalukantPredefineFirstname.create({ name: firstName, gender: gender }); + } + if (!lastNameObject) { + lastNameObject = await FalukantPredefineLastname.create({ name: lastName }); + } + const randomRegion = await RegionData.findOne({ + order: Sequelize.fn('RANDOM'), + limit: 1, + include: [ + { + model: RegionType, + as: 'regionType', + where: { + labelTr: 'city' + } + } + ] + }); + if (!randomRegion) { + throw new Error('No region found with the label "city".'); + } + const titleOfNobility = await TitleOfNobility.findOne({ where: { labelTr: 'noncivil' } }); + if (!titleOfNobility) { + throw new Error('No title of nobility found with the label "noncivil".'); + } + const falukantUser = await FalukantUser.create({ + userId: user.id, + money: 50.00, + creditAmount: 0.00, + todayCreditTaken: 0.00, + creditInterestRate: 0.00, + mainBranchRegionId: randomRegion.id, + }); + const fourteenDaysAgo = new Date(); + fourteenDaysAgo.setDate(fourteenDaysAgo.getDate() - 14); + const character = await FalukantCharacter.create({ + userId: falukantUser.id, + regionId: randomRegion.id, + firstName: firstNameObject.id, + lastName: lastNameObject.id, + gender: gender, + birthdate: fourteenDaysAgo, + titleOfNobility: titleOfNobility.id, + }); + await FalukantStock.create({ + userId: falukantUser.id, + regionId: randomRegion.id, + stockTypeId: (await FalukantStockType.findOne({ where: [{ label_tr: 'wood' }] })).id, + quantity: 10, + }); + falukantUser['character'] = character; + const branchType = await BranchType.findOne({ where: { labelTr: 'fullstack' } }); + await Branch.create({ + userId: falukantUser.id, + regionId: randomRegion.id, + branchTypeId: branchType.id, + }) + notifyUser(user.hashedId, 'reloadmenu', {}); + return falukantUser; + } catch (error) { + console.error('Error creating character'); + console.log(error); + } + } + + async getInfo(hashedUserId) { + try { + const user = await User.findOne({ where: { hashedId: hashedUserId } }); + if (!user) { + throw new Error('User not found'); + } + const falukantUser = await FalukantUser.findOne({ + include: [{ + model: User, + as: 'user', + attributes: ['hashedId'], + where: { + hashedId: hashedUserId + }, + }, + { + model: FalukantCharacter, + as: 'character', + attributes: ['birthdate', 'health'], + }, + ], + attributes: ['money'] + }); + if (!falukantUser) { + throw new Error('User not found'); + } + const character = falukantUser.character; + if (character && character.birthdate) { + const birthdate = new Date(character.birthdate); + birthdate.setHours(0); + birthdate.setMinutes(0); + const currentDate = new Date(); + currentDate.setHours(0); + currentDate.setMinutes(0); + const ageInDays = differenceInDays(currentDate, birthdate); + character.setDataValue('age', ageInDays); + } + return falukantUser; + } catch (error) { + console.error('Error getting character info'); + console.log(error); + } + } + + async getBranches(hashedUserId) { + try { + const falukantUser = await this.getFalukantUserByHashedId(hashedUserId); + if (!falukantUser) { + throw new Error('User not found'); + } + const branches = await Branch.findAll({ + where: { falukantUserId: falukantUser.id }, + include: [ + { + model: BranchType, + as: 'branchType', + attributes: ['labelTr'], + }, + { + model: RegionData, + as: 'region', + attributes: ['name'], + } + ], + attributes: ['id', 'regionId'], + order: [['branchTypeId', 'ASC']], + }); + const enrichedBranches = branches.map(branch => ({ + ...branch.toJSON(), + isMainBranch: falukantUser.mainBranchRegionId === branch.regionId, + })); + return enrichedBranches; + } catch (error) { + console.error('Error in getBranches:', error); + throw new Error('Failed to retrieve branches'); + } + } + } export default new FalukantService(); diff --git a/backend/utils/falukant/initializeFalukantPredefines.js b/backend/utils/falukant/initializeFalukantPredefines.js new file mode 100644 index 0000000..d9a4d99 --- /dev/null +++ b/backend/utils/falukant/initializeFalukantPredefines.js @@ -0,0 +1,341 @@ +import FalukantPredefineFirstname from "../../models/falukant/predefine/firstname.js"; +import FalukantPredefineLastname from "../../models/falukant/predefine/lastname.js"; +import BranchType from "../../models/falukant/type/branch.js"; +import ProductType from "../../models/falukant/type/product.js"; +import FalukantStockType from "../../models/falukant/type/stock.js"; +import TitleOfNobility from "../../models/falukant/type/title_of_nobility.js"; +import TitleRequirement from "../../models/falukant/type/title_requirement.js"; + +export const initializeFalukantPredefines = async () => { + await initializeFalukantFirstnames(); + await initializeFalukantLastnames(); + await initializeFalukantStockTypes(); + await initializeFalukantProducts(); + await initializeFalukantTitles(); + await initializeFalukantTitleRequirements(); + await initializeFalukantBranchTypes(); +} + +const initializeFalukantFirstnames = async () => { + await FalukantPredefineFirstname.bulkCreate([ + { name: "Alexander", gender: "male" }, + { name: "Ben", gender: "male" }, + { name: "Christian", gender: "male" }, + { name: "Daniel", gender: "male" }, + { name: "Elias", gender: "male" }, + { name: "Felix", gender: "male" }, + { name: "Gabriel", gender: "male" }, + { name: "Hans", gender: "male" }, + { name: "Ismail", gender: "male" }, + { name: "Jakob", gender: "male" }, + { name: "Kai", gender: "male" }, + { name: "Lukas", gender: "male" }, + { name: "Maximilian", gender: "male" }, + { name: "Niklas", gender: "male" }, + { name: "Oliver", gender: "male" }, + { name: "Paul", gender: "male" }, + { name: "Quentin", gender: "male" }, + { name: "Robert", gender: "male" }, + { name: "Sebastian", gender: "male" }, + { name: "Thomas", gender: "male" }, + { name: "Ulf", gender: "male" }, + { name: "Vincent", gender: "male" }, + { name: "Thorsten", gender: "male" }, + { name: "Ulrich", gender: "male" }, + { name: "Torben", gender: "male" }, + { name: "Torsten", gender: "male" }, + { name: "Uwe", gender: "male" }, + { name: "Viktor", gender: "male" }, + { name: "Wolfgang", gender: "male" }, + { name: "Xaver", gender: "male" }, + { name: "Yannik", gender: "male" }, + { name: "Zacharias", gender: "male" }, + { name: "Aaron", gender: "male" }, + { name: "Bruno", gender: "male" }, + { name: "Carl", gender: "male" }, + { name: "David", gender: "male" }, + { name: "Emil", gender: "male" }, + { name: "Fabian", gender: "male" }, + { name: "Georg", gender: "male" }, + { name: "Heinrich", gender: "male" }, + { name: "Ian", gender: "male" }, + { name: "Jonas", gender: "male" }, + { name: "Karl", gender: "male" }, + { name: "Leon", gender: "male" }, + { name: "Matthias", gender: "male" }, + { name: "Nils", gender: "male" }, + { name: "Oskar", gender: "male" }, + { name: "Peter", gender: "male" }, + { name: "Ralf", gender: "male" }, + { name: "Simon", gender: "male" }, + { name: "Tobias", gender: "male" }, + { name: "Ulrich", gender: "male" }, + { name: "Vince", gender: "male" }, + { name: "Walter", gender: "male" }, + { name: "Xeno", gender: "male" }, + { name: "Yves", gender: "male" }, + { name: "Zeno", gender: "male" }, + { name: "Anna", gender: "female" }, + { name: "Berit", gender: "female" }, + { name: "Charlotte", gender: "female" }, + { name: "Diana", gender: "female" }, + { name: "Emilia", gender: "female" }, + { name: "Fiona", gender: "female" }, + { name: "Greta", gender: "female" }, + { name: "Hanna", gender: "female" }, + { name: "Isabelle", gender: "female" }, + { name: "Johanna", gender: "female" }, + { name: "Katharina", gender: "female" }, + { name: "Lena", gender: "female" }, + { name: "Marie", gender: "female" }, + { name: "Nina", gender: "female" }, + { name: "Olivia", gender: "female" }, + { name: "Paula", gender: "female" }, + { name: "Quirina", gender: "female" }, + { name: "Rebecca", gender: "female" }, + { name: "Sophia", gender: "female" }, + { name: "Theresa", gender: "female" }, + { name: "Ulrike", gender: "female" }, + { name: "Valeria", gender: "female" }, + { name: "Wilma", gender: "female" }, + { name: "Xenia", gender: "female" }, + { name: "Yara", gender: "female" }, + { name: "Zoe", gender: "female" }, + { name: "Antonia", gender: "female" }, + { name: "Beate", gender: "female" }, + { name: "Carla", gender: "female" }, + { name: "Dorothea", gender: "female" }, + { name: "Elisabeth", gender: "female" }, + { name: "Franziska", gender: "female" }, + { name: "Gerda", gender: "female" }, + { name: "Helena", gender: "female" }, + { name: "Irene", gender: "female" }, + { name: "Julia", gender: "female" }, + { name: "Klara", gender: "female" }, + { name: "Leonie", gender: "female" }, + { name: "Marlene", gender: "female" }, + { name: "Nele", gender: "female" }, + { name: "Petra", gender: "female" }, + { name: "Renate", gender: "female" }, + { name: "Sandra", gender: "female" }, + { name: "Tanja", gender: "female" }, + { name: "Ursula", gender: "female" }, + { name: "Vanessa", gender: "female" }, + { name: "Waltraud", gender: "female" }, + { name: "Xaveria", gender: "female" }, + { name: "Yvonne", gender: "female" }, + { name: "Zora", gender: "female" }, + ], { + ignoreDuplicates: true, + }); +} + +const initializeFalukantLastnames = async () => { + await FalukantPredefineLastname.bulkCreate([ + { name: "Adler" }, + { name: "Bauer" }, + { name: "Becker" }, + { name: "Bergmann" }, + { name: "Braun" }, + { name: "Busch" }, + { name: "Dreyer" }, + { name: "Eberhardt" }, + { name: "Fischer" }, + { name: "Franke" }, + { name: "Friedrich" }, + { name: "Geiger" }, + { name: "Gärtner" }, + { name: "Hartmann" }, + { name: "Hoffmann" }, + { name: "Hofmann" }, + { name: "Horn" }, + { name: "Huber" }, + { name: "Jäger" }, + { name: "Jung" }, + { name: "Kaiser" }, + { name: "Keller" }, + { name: "Klein" }, + { name: "Koch" }, + { name: "König" }, + { name: "Krüger" }, + { name: "Lang" }, + { name: "Lehmann" }, + { name: "Ludwig" }, + { name: "Maier" }, + { name: "Meyer" }, + { name: "Müller" }, + { name: "Neumann" }, + { name: "Neff" }, + { name: "Obermeier" }, + { name: "Otto" }, + { name: "Peters" }, + { name: "Ritter" }, + { name: "Richter" }, + { name: "Rosen" }, + { name: "Schäfer" }, + { name: "Schmidt" }, + { name: "Schneider" }, + { name: "Schulz" }, + { name: "Schulze" }, + { name: "Schwarz" }, + { name: "Schuster" }, + { name: "Sommer" }, + { name: "Stein" }, + { name: "Tanner" }, + { name: "Thiel" }, + { name: "Ullmann" }, + { name: "Ullrich" }, + { name: "Vogel" }, + { name: "Voigt" }, + { name: "Wagner" }, + { name: "Walter" }, + { name: "Weber" }, + { name: "Weiß" }, + { name: "Winter" }, + { name: "Wolf" }, + { name: "Xaver" }, + { name: "Xavier" }, + { name: "Zimmer" }, + { name: "Zimmermann" }, + { name: "Albrecht" }, + { name: "Arnold" }, + { name: "Baumann" }, + { name: "Dietrich" }, + { name: "Engel" }, + { name: "Graf" }, + { name: "Kirsch" }, + { name: "Lenz" }, + { name: "Schirmer" }, + { name: "Vogt" }, + { name: "Ziegler" }, + ], { + ignoreDuplicates: true, + }); +} + +async function initializeFalukantStockTypes() { + await FalukantStockType.bulkCreate([ + { labelTr: 'wood', cost: 15 }, + { labelTr: 'stone', cost: 25 }, + { labelTr: 'iron', cost: 100 }, + { labelTr: 'field', cost: 5 }, + ]); +} + +async function initializeFalukantProducts() { + await ProductType.bulkCreate([ + { labelTr: 'wheat', category: 1, productionTime: 2, sellCost: 7 }, + { labelTr: 'grain', category: 1, productionTime: 2, sellCost: 7 }, + { labelTr: 'carrot', category: 1, productionTime: 1, sellCost: 4 }, + { labelTr: 'fish', category: 1, productionTime: 2, sellCost: 7 }, + { labelTr: 'meat', category: 1, productionTime: 2, sellCost: 7 }, + { labelTr: 'leather', category: 1, productionTime: 2, sellCost: 7 }, + { labelTr: 'wood', category: 1, productionTime: 2, sellCost: 7 }, + { labelTr: 'stone', category: 1, productionTime: 2, sellCost: 7 }, + { labelTr: 'milk', category: 1, productionTime: 1, sellCost: 4 }, + { labelTr: 'cheese', category: 1, productionTime: 1, sellCost: 4 }, + { labelTr: 'bread', category: 1, productionTime: 1, sellCost: 4 }, + { labelTr: 'beer', category: 1, productionTime: 1, sellCost: 4 }, + { labelTr: 'iron', category: 2, productionTime: 4, sellCost: 15 }, + { labelTr: 'copper', category: 2, productionTime: 4, sellCost: 15 }, + { labelTr: 'spices', category: 2, productionTime: 8, sellCost: 30 }, + { labelTr: 'salt', category: 2, productionTime: 4, sellCost: 15 }, + { labelTr: 'sugar', category: 2, productionTime: 4, sellCost: 15 }, + { labelTr: 'vinegar', category: 2, productionTime: 4, sellCost: 15 }, + { labelTr: 'cotton', category: 2, productionTime: 4, sellCost: 15 }, + { labelTr: 'wine', category: 2, productionTime: 4, sellCost: 15 }, + { labelTr: 'gold', category: 3, productionTime: 4, sellCost: 30 }, + { labelTr: 'diamond', category: 3, productionTime: 4, sellCost: 30 }, + { labelTr: 'furniture', category: 3, productionTime: 4, sellCost: 30 }, + { labelTr: 'clothing', category: 3, productionTime: 4, sellCost: 30 }, + { labelTr: 'jewelry', category: 4, productionTime: 5, sellCost: 60 }, + { labelTr: 'painting', category: 4, productionTime: 5, sellCost: 60 }, + { labelTr: 'book', category: 4, productionTime: 5, sellCost: 60 }, + { labelTr: 'weapon', category: 4, productionTime: 5, sellCost: 60 }, + { labelTr: 'armor', category: 4, productionTime: 5, sellCost: 60 }, + { labelTr: 'shield', category: 4, productionTime: 5, sellCost: 60 }, + { labelTr: 'horse', category: 5, productionTime: 5, sellCost: 60 }, + { labelTr: 'ox', category: 5, productionTime: 5, sellCost: 60 }, + ], { + ignoreDuplicates: true, + }); +} + +async function initializeFalukantTitles() { + await TitleOfNobility.bulkCreate([ + { labelTr: "noncivil" }, + { labelTr: "civil" }, + { labelTr: "sir" }, + { labelTr: "townlord" }, + { labelTr: "by" }, + { labelTr: "landlord" }, + { labelTr: "knight" }, + { labelTr: "baron" }, + { labelTr: "count" }, + { labelTr: "palsgrave" }, + { labelTr: "margrave" }, + { labelTr: "landgrave" }, + { labelTr: "ruler" }, + { labelTr: "elector" }, + { labelTr: "imperial-prince" }, + { labelTr: "duke" }, + { labelTr: "grand-duke" }, + { labelTr: "prince-regent" }, + { labelTr: "king" }, + ], { + updateOnDuplicate: ['labelTr'], + }); +} + +async function initializeFalukantTitleRequirements() { + const titleRequirements = [ + { labelTr: "civil", requirements: [{ type: "money", value: 500 }] }, + { labelTr: "sir", requirements: [{ type: "branches", value: 2 }] }, + { labelTr: "townlord", requirements: [] }, + { labelTr: "by", requirements: [] }, + { labelTr: "landlord", requirements: [] }, + { labelTr: "knight", requirements: [] }, + { labelTr: "baron", requirements: [{ type: "branches", value: 4 }] }, + { labelTr: "count", requirements: [] }, + { labelTr: "palsgrave", requirements: [] }, + { labelTr: "margrave", requirements: [] }, + { labelTr: "landgrave", requirements: [] }, + { labelTr: "ruler", requirements: [] }, + { labelTr: "elector", requirements: [] }, + { labelTr: "imperial-prince", requirements: [] }, + { labelTr: "duke", requirements: [] }, + { labelTr: "grand-duke", requirements: [] }, + { labelTr: "prince-regent", requirements: [] }, + { labelTr: "king", requirements: [] }, + ]; + const titles = await TitleOfNobility.findAll(); + const requirementsToInsert = []; + for (let i = 0; i < titleRequirements.length; i++) { + const titleRequirement = titleRequirements[i]; + const title = titles.find(t => t.labelTr === titleRequirement.labelTr); + if (!title) continue; + if (i > 1) { + const moneyRequirement = { + type: "money", + value: 5000 * Math.pow(3, i - 1), + }; + titleRequirement.requirements.push(moneyRequirement); + } + for (const requirement of titleRequirement.requirements) { + requirementsToInsert.push({ + titleId: title.id, + requirementType: requirement.type, + requirementValue: requirement.value, + }); + } + } + await TitleRequirement.bulkCreate(requirementsToInsert, { ignoreDuplicates: true }); +} + +async function initializeFalukantBranchTypes() { + await BranchType.bulkCreate([ + { labelTr: 'production', baseCost: 3000 }, + { labelTr: 'store', baseCost: 2000 }, + { labelTr: 'fullstack', baseCost: 4500}, + ], { ignoreDuplicates: true }); +} \ No newline at end of file diff --git a/backend/utils/initializeFalukant.js b/backend/utils/initializeFalukant.js index c984cb3..37337b0 100644 --- a/backend/utils/initializeFalukant.js +++ b/backend/utils/initializeFalukant.js @@ -1,8 +1,10 @@ import { initializeFalukantTypes, initializeFalukantRegions } from './falukant/initializeFalukantTypes.js'; +import { initializeFalukantPredefines } from './falukant/initializeFalukantPredefines.js'; const initializeFalukant = async () => { await initializeFalukantTypes(); await initializeFalukantRegions(); + await initializeFalukantPredefines(); } export default initializeFalukant; \ No newline at end of file diff --git a/backend/utils/sequelize.js b/backend/utils/sequelize.js index 919ad91..5b66074 100644 --- a/backend/utils/sequelize.js +++ b/backend/utils/sequelize.js @@ -19,6 +19,7 @@ const createSchemas = async () => { await sequelize.query('CREATE SCHEMA IF NOT EXISTS forum'); await sequelize.query('CREATE SCHEMA IF NOT EXISTS falukant_data'); await sequelize.query('CREATE SCHEMA IF NOT EXISTS falukant_type'); + await sequelize.query('CREATE SCHEMA IF NOT EXISTS falukant_predefine'); }; const initializeDatabase = async () => { diff --git a/frontend/public/images/falukant/avatar/female.png b/frontend/public/images/falukant/avatar/female.png new file mode 100644 index 0000000..af8452a Binary files /dev/null and b/frontend/public/images/falukant/avatar/female.png differ diff --git a/frontend/public/images/falukant/avatar/male.png b/frontend/public/images/falukant/avatar/male.png new file mode 100644 index 0000000..fcb4388 Binary files /dev/null and b/frontend/public/images/falukant/avatar/male.png differ diff --git a/frontend/public/images/icons/falukant/bank.png b/frontend/public/images/icons/falukant/bank.png new file mode 100644 index 0000000..78a6370 Binary files /dev/null and b/frontend/public/images/icons/falukant/bank.png differ diff --git a/frontend/public/images/icons/falukant/branches.png b/frontend/public/images/icons/falukant/branches.png new file mode 100644 index 0000000..3bccfc0 Binary files /dev/null and b/frontend/public/images/icons/falukant/branches.png differ diff --git a/frontend/public/images/icons/falukant/darknet.png b/frontend/public/images/icons/falukant/darknet.png new file mode 100644 index 0000000..3ae8b0f Binary files /dev/null and b/frontend/public/images/icons/falukant/darknet.png differ diff --git a/frontend/public/images/icons/falukant/director.png b/frontend/public/images/icons/falukant/director.png new file mode 100644 index 0000000..effa2d1 Binary files /dev/null and b/frontend/public/images/icons/falukant/director.png differ diff --git a/frontend/public/images/icons/falukant/events.png b/frontend/public/images/icons/falukant/events.png new file mode 100644 index 0000000..9e9a640 Binary files /dev/null and b/frontend/public/images/icons/falukant/events.png differ diff --git a/frontend/public/images/icons/falukant/family.png b/frontend/public/images/icons/falukant/family.png new file mode 100644 index 0000000..c2eb482 Binary files /dev/null and b/frontend/public/images/icons/falukant/family.png differ diff --git a/frontend/public/images/icons/falukant/house.png b/frontend/public/images/icons/falukant/house.png new file mode 100644 index 0000000..278b673 Binary files /dev/null and b/frontend/public/images/icons/falukant/house.png differ diff --git a/frontend/public/images/icons/falukant/moneyhistory.png b/frontend/public/images/icons/falukant/moneyhistory.png new file mode 100644 index 0000000..6c9b5e6 Binary files /dev/null and b/frontend/public/images/icons/falukant/moneyhistory.png differ diff --git a/frontend/public/images/icons/falukant/overview.png b/frontend/public/images/icons/falukant/overview.png new file mode 100644 index 0000000..4978e00 Binary files /dev/null and b/frontend/public/images/icons/falukant/overview.png differ diff --git a/frontend/public/images/icons/falukant/politics.png b/frontend/public/images/icons/falukant/politics.png new file mode 100644 index 0000000..8084d4f Binary files /dev/null and b/frontend/public/images/icons/falukant/politics.png differ diff --git a/frontend/public/images/icons/falukant/reputation.png b/frontend/public/images/icons/falukant/reputation.png new file mode 100644 index 0000000..2cad4a0 Binary files /dev/null and b/frontend/public/images/icons/falukant/reputation.png differ diff --git a/frontend/public/images/icons/falukant/schooling.png b/frontend/public/images/icons/falukant/schooling.png new file mode 100644 index 0000000..bce6cca Binary files /dev/null and b/frontend/public/images/icons/falukant/schooling.png differ diff --git a/frontend/public/images/icons/falukant/title.png b/frontend/public/images/icons/falukant/title.png new file mode 100644 index 0000000..d37fa8f Binary files /dev/null and b/frontend/public/images/icons/falukant/title.png differ diff --git a/frontend/public/images/icons/falukant/workshop.png b/frontend/public/images/icons/falukant/workshop.png new file mode 100644 index 0000000..9d91894 Binary files /dev/null and b/frontend/public/images/icons/falukant/workshop.png differ diff --git a/frontend/src/components/AppNavigation.vue b/frontend/src/components/AppNavigation.vue index 5e58ccb..fafdd40 100644 --- a/frontend/src/components/AppNavigation.vue +++ b/frontend/src/components/AppNavigation.vue @@ -70,6 +70,9 @@ export default { newValue.on('friendloginchanged', () => { this.fetchFriends(); }); + newValue.on('reloadmenu', () => { + this.loadMenu(); + }) } } }, @@ -84,12 +87,10 @@ export default { }, mounted() { if (this.$store.getters.socket) { - console.log('connect sockets in navigation') this.$store.getters.socket.on('forumschanged', (data) => { this.fetchForums(); }); this.$store.getters.socket.on('friendloginchanged', () => { - console.log('update friends'); this.fetchFriends(); }); } diff --git a/frontend/src/components/falukant/StatusBar.vue b/frontend/src/components/falukant/StatusBar.vue new file mode 100644 index 0000000..a592a3f --- /dev/null +++ b/frontend/src/components/falukant/StatusBar.vue @@ -0,0 +1,94 @@ + + + + + diff --git a/frontend/src/components/form/FormattedDropdown.vue b/frontend/src/components/form/FormattedDropdown.vue new file mode 100644 index 0000000..4daee1c --- /dev/null +++ b/frontend/src/components/form/FormattedDropdown.vue @@ -0,0 +1,133 @@ + + + + + diff --git a/frontend/src/i18n/index.js b/frontend/src/i18n/index.js index b627d83..8e9edcb 100644 --- a/frontend/src/i18n/index.js +++ b/frontend/src/i18n/index.js @@ -13,6 +13,7 @@ import enSettings from './locales/en/settings.json'; import enAdmin from './locales/en/admin.json'; import enSocialNetwork from './locales/en/socialnetwork.json'; import enFriends from './locales/en/friends.json'; +import enFalukant from './locales/en/falukant.json'; import deGeneral from './locales/de/general.json'; import deHeader from './locales/de/header.json'; @@ -26,6 +27,7 @@ import deSettings from './locales/de/settings.json'; import deAdmin from './locales/de/admin.json'; import deSocialNetwork from './locales/de/socialnetwork.json'; import deFriends from './locales/de/friends.json'; +import deFalukant from './locales/de/falukant.json'; const messages = { en: { @@ -41,6 +43,7 @@ const messages = { ...enAdmin, ...enSocialNetwork, ...enFriends, + ...enFalukant, }, de: { 'Ok': 'Ok', @@ -56,6 +59,7 @@ const messages = { ...deAdmin, ...deSocialNetwork, ...deFriends, + ...deFalukant, } }; diff --git a/frontend/src/i18n/locales/de/falukant.json b/frontend/src/i18n/locales/de/falukant.json index 43fb193..f13c4fc 100644 --- a/frontend/src/i18n/locales/de/falukant.json +++ b/frontend/src/i18n/locales/de/falukant.json @@ -1,5 +1,130 @@ { "falukant": { - + "statusbar": { + "age": "Alter", + "wealth": "Vermögen", + "health": "Gesundheit", + "events": "Ereignisse" + }, + "health": { + "amazing": "Super", + "good": "Gut", + "normal": "Normal", + "bad": "Schlecht", + "very_bad": "Sehr schlecht" + }, + "create": { + "title": "Am Spiel teilnehmen", + "gender": "Geschlecht", + "male": "Mann", + "female": "Frau", + "firstname": "Vorname", + "lastname": "Nachname", + "random": "Zufällig", + "submit": "Teilnehmen" + }, + "overview": { + "title": "Falukant - Übersicht", + "metadata": { + "title": "Persönliches", + "name": "Name", + "money": "Vermögen", + "age": "Alter", + "mainbranch": "Heimatstadt" + }, + "productions": { + "title": "Produktionen" + }, + "stock": { + "title": "Lager" + }, + "branches": { + "title": "Filialen", + "level": { + "production": "Produktion", + "store": "Verkauf", + "fullstack": "Produktion mit Verkauf" + } + } + }, + "titles": { + "male": { + "noncivil": "Leibeigener", + "civil": "Bürgerlich", + "sir": "Herr", + "townlord": "Stadtherr", + "by": "von", + "landlord": "Landherr", + "knight": "Ritter", + "baron": "Baron", + "count": "Graf", + "palsgrave": "Pfalzgraf", + "margrave": "Markgraf", + "landgrave": "Landgraf", + "ruler": "Fürst", + "elector": "Kurfürst", + "imperial-prince": "Reichsfürst", + "duke": "Herzog", + "grand-duke": "Großherzog", + "prince-regent": "Prinzregent", + "king": "König" + }, + "female": { + "noncivil": "Leibeigene", + "civil": "Bürgerlich", + "sir": "Frau", + "townlord": "Stadtherrin", + "by": "zu", + "landlord": "Landherrin", + "knight": "Freifrau", + "baron": "Baronin", + "count": "Gräfin", + "palsgrave": "Pfalzgräfin", + "margrave": "Markgräfin", + "landgrave": "Landgräfin", + "ruler": "Fürstin", + "elector": "Kurfürstin", + "imperial-prince": "Reichsfürstin", + "duke": "Herzogin", + "grand-duke": "Großherzogin", + "prince-regent": "Prinzregentin", + "king": "Königin" + } + }, + "branch": { + "title": "Filiale", + "selection": { + "title": "Niederlassungsauswahl", + "selected": "Ausgewählte Niederlassung", + "placeholder": "Noch keine Niederlassung ausgewählt" + }, + "actions": { + "create": "Neue Niederlassung erstellen", + "upgrade": "Aktuelle Niederlassung aufwerten", + "createAlert": "Neue Niederlassung wird erstellt.", + "upgradeAlert": "Die Niederlassung mit der ID {branchId} wird aufgewertet." + }, + "director": { + "title": "Direktor-Infos", + "info": "Informationen über den Direktor der Niederlassung." + }, + "sale": { + "title": "Verkauf", + "info": "Hier können Produkte verkauft werden." + }, + "production": { + "title": "Produktion", + "info": "Details zur Produktion in der Niederlassung." + }, + "columns": { + "city": "Stadt", + "type": "Typ" + }, + "types": { + "production": "Produktion", + "store": "Verkauf", + "fullstack": "Produktion mit Verkauf" + } + } } } \ No newline at end of file diff --git a/frontend/src/i18n/locales/de/navigation.json b/frontend/src/i18n/locales/de/navigation.json index 5f3a8b3..1a54d5d 100644 --- a/frontend/src/i18n/locales/de/navigation.json +++ b/frontend/src/i18n/locales/de/navigation.json @@ -55,7 +55,12 @@ "house": "Haus", "darknet": "Untergrund", "reputation": "Reputation", - "moneyhistory": "Geldfluss" + "moneyhistory": "Geldfluss", + "nobility": "Sozialstatus", + "politics": "Politik", + "education": "Bildung", + "health": "Gesundheit", + "bank": "Bank" } } } diff --git a/frontend/src/router/adminRoutes.js b/frontend/src/router/adminRoutes.js new file mode 100644 index 0000000..eae4dbc --- /dev/null +++ b/frontend/src/router/adminRoutes.js @@ -0,0 +1,26 @@ +import AdminInterestsView from '../views/admin/InterestsView.vue'; +import AdminContactsView from '../views/admin/ContactsView.vue'; +import ForumAdminView from '../dialogues/admin/ForumAdminView.vue'; + +const adminRoutes = [ + { + path: '/admin/interests', + name: 'AdminInterests', + component: AdminInterestsView, + meta: { requiresAuth: true } + }, + { + path: '/admin/contacts', + name: 'AdminContacts', + component: AdminContactsView, + meta: { requiresAuth: true } + }, + { + path: '/admin/forum', + name: 'AdminForums', + component: ForumAdminView, + meta: { requiresAuth: true } + }, +]; + +export default adminRoutes; diff --git a/frontend/src/router/authRoutes.js b/frontend/src/router/authRoutes.js new file mode 100644 index 0000000..35c546f --- /dev/null +++ b/frontend/src/router/authRoutes.js @@ -0,0 +1,11 @@ +import ActivateView from '../views/auth/ActivateView.vue'; + +const authRoutes = [ + { + path: '/activate', + name: 'Activate page', + component: ActivateView + }, +]; + +export default authRoutes; diff --git a/frontend/src/router/falukantRoutes.js b/frontend/src/router/falukantRoutes.js new file mode 100644 index 0000000..225146c --- /dev/null +++ b/frontend/src/router/falukantRoutes.js @@ -0,0 +1,26 @@ +import BranchView from '../views/falukant/BranchView.vue'; +import Createview from '../views/falukant/CreateView.vue'; +import FalukantOverviewView from '../views/falukant/OverviewView.vue'; + +const falukantRoutes = [ + { + path: '/falukant/create', + name: 'FalukantCreate', + component: Createview, + meta: { requiresAuth: true } + }, + { + path: '/falukant/home', + name: 'FalukantOverview', + component: FalukantOverviewView, + meta: { requiresAuth: true } + }, + { + path: '/falukant/branch/:branchId?', + name: 'BranchView', + component: BranchView, + meta: { requiresAuth: true }, + }, +]; + +export default falukantRoutes; diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index 87efb2e..88dddc9 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -1,134 +1,25 @@ import { createRouter, createWebHistory } from 'vue-router'; import store from '../store'; import HomeView from '../views/HomeView.vue'; -import ActivateView from '../views/auth/ActivateView.vue'; -import PeronalSettingsView from '../views/settings/PersonalView.vue'; -import ViewSettingsView from '../views/settings/ViewView.vue'; -import FlirtSettingsView from '../views/settings/FlirtView.vue'; -import SexualitySettingsView from '../views/settings/SexualityView.vue'; -import AccountSettingsView from '../views/settings/AccountView.vue'; -import InterestsView from '../views/settings/InterestsView.vue'; -import AdminInterestsView from '../views/admin/InterestsView.vue'; -import AdminContactsView from '../views/admin/ContactsView.vue'; -import SearchView from '../views/social/SearchView.vue'; -import GalleryView from '../views/social/GalleryView.vue'; -import GuestbookView from '../views/social/GuestbookView.vue'; -import DiaryView from '../views/social/DiaryView.vue'; -import ForumAdminView from '../dialogues/admin/ForumAdminView.vue'; -import ForumView from '../views/social/ForumView.vue'; -import ForumTopicView from '../views/social/ForumTopicView.vue'; -import FriendsView from '../views/social/FriendsView.vue'; +import authRoutes from './authRoutes'; +import socialRoutes from './socialRoutes'; +import settingsRoutes from './settingsRoutes'; +import adminRoutes from './adminRoutes'; +import falukantRoutes from './falukantRoutes'; const routes = [ { - path: '/', + path: '/', name: 'Home', component: HomeView }, - { - path: '/activate', - name: 'Activate page', - component: ActivateView - }, - { - path: '/friends', - name: 'Friends', - component: FriendsView, - meta: { requiresAuth: true } - }, - { - path: '/socialnetwork/guestbook', - name: 'Guestbook', - component: GuestbookView, - meta: { requiresAuth: true } - }, - { - path: '/socialnetwork/search', - name: 'Search users', - component: SearchView, - meta: { requiresAuth: true } - }, - { - path: '/socialnetwork/gallery', - name: 'Gallery', - component: GalleryView, - meta: { requiresAuth: true } - }, - { - path: '/socialnetwork/forum/:id', - name: 'Forum', - component: ForumView, - meta: { requiresAuth: true } - }, - { - path: '/socialnetwork/forumtopic/:id', - name: 'ForumTopic', - component: ForumTopicView, - meta: { requiresAuth: true } - }, - { - path: '/socialnetwork/diary', - name: 'Diary', - component: DiaryView, - meta: { requiresAuth: true } - }, - { - path: '/settings/personal', - name: 'Personal settings', - component: PeronalSettingsView, - meta: { requiresAuth: true } - }, - { - path: '/settings/view', - name: 'View settings', - component: ViewSettingsView, - meta: { requiresAuth: true } - }, - { - path: '/settings/sexuality', - name: 'Sexuality settings', - component: SexualitySettingsView, - meta: { requiresAuth: true } - }, - { - path: '/settings/flirt', - name: 'Flirt settings', - component: FlirtSettingsView, - meta: { requiresAuth: true } - }, - { - path: '/settings/account', - name: 'Account settings', - component: AccountSettingsView, - meta: { requiresAuth: true } - }, - { - path: '/settings/interests', - name: 'Interests', - component: InterestsView, - meta: { requiresAuth: true } - }, - { - path: '/admin/interests', - name: 'AdminInterests', - component: AdminInterestsView, - meta: { requiresAuth: true } - }, - { - path: '/admin/contacts', - name: 'AdminContacts', - component: AdminContactsView, - meta: { requiresAuth: true } - }, - { - path: '/admin/forum', - name: 'AdminForums', - component: ForumAdminView, - meta: { requiresAuth: true } - } + ...authRoutes, + ...socialRoutes, + ...settingsRoutes, + ...adminRoutes, + ...falukantRoutes, ]; - const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes @@ -149,4 +40,3 @@ router.beforeEach((to, from, next) => { }); export default router; - diff --git a/frontend/src/router/settingsRoutes.js b/frontend/src/router/settingsRoutes.js new file mode 100644 index 0000000..fbf0a95 --- /dev/null +++ b/frontend/src/router/settingsRoutes.js @@ -0,0 +1,47 @@ +import PeronalSettingsView from '../views/settings/PersonalView.vue'; +import ViewSettingsView from '../views/settings/ViewView.vue'; +import FlirtSettingsView from '../views/settings/FlirtView.vue'; +import SexualitySettingsView from '../views/settings/SexualityView.vue'; +import AccountSettingsView from '../views/settings/AccountView.vue'; +import InterestsView from '../views/settings/InterestsView.vue'; + +const settingsRoutes = [ + { + path: '/settings/personal', + name: 'Personal settings', + component: PeronalSettingsView, + meta: { requiresAuth: true } + }, + { + path: '/settings/view', + name: 'View settings', + component: ViewSettingsView, + meta: { requiresAuth: true } + }, + { + path: '/settings/sexuality', + name: 'Sexuality settings', + component: SexualitySettingsView, + meta: { requiresAuth: true } + }, + { + path: '/settings/flirt', + name: 'Flirt settings', + component: FlirtSettingsView, + meta: { requiresAuth: true } + }, + { + path: '/settings/account', + name: 'Account settings', + component: AccountSettingsView, + meta: { requiresAuth: true } + }, + { + path: '/settings/interests', + name: 'Interests', + component: InterestsView, + meta: { requiresAuth: true } + }, +]; + +export default settingsRoutes; diff --git a/frontend/src/router/socialRoutes.js b/frontend/src/router/socialRoutes.js new file mode 100644 index 0000000..eb51f0c --- /dev/null +++ b/frontend/src/router/socialRoutes.js @@ -0,0 +1,54 @@ +import FriendsView from '../views/social/FriendsView.vue'; +import SearchView from '../views/social/SearchView.vue'; +import GalleryView from '../views/social/GalleryView.vue'; +import GuestbookView from '../views/social/GuestbookView.vue'; +import DiaryView from '../views/social/DiaryView.vue'; +import ForumView from '../views/social/ForumView.vue'; +import ForumTopicView from '../views/social/ForumTopicView.vue'; + +const socialRoutes = [ + { + path: '/friends', + name: 'Friends', + component: FriendsView, + meta: { requiresAuth: true } + }, + { + path: '/socialnetwork/guestbook', + name: 'Guestbook', + component: GuestbookView, + meta: { requiresAuth: true } + }, + { + path: '/socialnetwork/search', + name: 'Search users', + component: SearchView, + meta: { requiresAuth: true } + }, + { + path: '/socialnetwork/gallery', + name: 'Gallery', + component: GalleryView, + meta: { requiresAuth: true } + }, + { + path: '/socialnetwork/forum/:id', + name: 'Forum', + component: ForumView, + meta: { requiresAuth: true } + }, + { + path: '/socialnetwork/forumtopic/:id', + name: 'ForumTopic', + component: ForumTopicView, + meta: { requiresAuth: true } + }, + { + path: '/socialnetwork/diary', + name: 'Diary', + component: DiaryView, + meta: { requiresAuth: true } + }, +]; + +export default socialRoutes; diff --git a/frontend/src/store/index.js b/frontend/src/store/index.js index f243085..0391214 100644 --- a/frontend/src/store/index.js +++ b/frontend/src/store/index.js @@ -7,10 +7,10 @@ import { io } from 'socket.io-client'; const store = createStore({ state: { - isLoggedIn: false, - user: null, + isLoggedIn: localStorage.getItem('isLoggedIn') === 'true', + user: JSON.parse(localStorage.getItem('user')) || null, language: navigator.language.startsWith('de') ? 'de' : 'en', - menu: [], + menu: JSON.parse(localStorage.getItem('menu')) || [], socket: null, menuNeedsUpdate: false, }, @@ -32,19 +32,22 @@ const store = createStore({ localStorage.removeItem('user'); localStorage.removeItem('menu'); state.menuNeedsUpdate = false; - // await apiClient.get('/api/auth/logout'); }, setLanguage(state, language) { state.language = language; }, setMenu(state, menu) { state.menu = menu; + localStorage.setItem('menu', JSON.stringify(menu)); state.menuNeedsUpdate = false; }, setSocket(state, socket) { state.socket = socket; }, clearSocket(state) { + if (state.socket) { + state.socket.disconnect(); + } state.socket = null; }, }, @@ -58,17 +61,20 @@ const store = createStore({ } await dispatch('loadMenu'); }, - logout({ commit, state }) { - if (state.socket) { - state.socket.disconnect(); - commit('clearSocket'); - } + logout({ commit }) { + commit('clearSocket'); commit('dologout'); router.push('/'); }, initializeSocket({ commit, state }) { if (state.isLoggedIn && state.user) { const socket = io(import.meta.env.VITE_API_BASE_URL); + socket.on('connect', () => { + socket.emit('setUserId', state.user.id); + }); + socket.on('disconnect', (reason) => { + console.warn('WebSocket disconnected:', reason); + }); commit('setSocket', socket); } }, @@ -90,12 +96,16 @@ const store = createStore({ user: state => state.user, language: state => state.language, menu: state => state.menu, - socket: state => state.socket, - menuNeedsUpdate: state => state.menuNeedsUpdate + socket: state => state.socket, + menuNeedsUpdate: state => state.menuNeedsUpdate, }, modules: { dialogs, }, }); +if (store.state.isLoggedIn && store.state.user) { + store.dispatch('initializeSocket'); +} + export default store; diff --git a/frontend/src/views/falukant/BranchView.vue b/frontend/src/views/falukant/BranchView.vue new file mode 100644 index 0000000..344ef04 --- /dev/null +++ b/frontend/src/views/falukant/BranchView.vue @@ -0,0 +1,130 @@ + + + + + diff --git a/frontend/src/views/falukant/CreateView.vue b/frontend/src/views/falukant/CreateView.vue new file mode 100644 index 0000000..70ed65b --- /dev/null +++ b/frontend/src/views/falukant/CreateView.vue @@ -0,0 +1,118 @@ + + + + + + diff --git a/frontend/src/views/falukant/OverviewView.vue b/frontend/src/views/falukant/OverviewView.vue new file mode 100644 index 0000000..23b2d5d --- /dev/null +++ b/frontend/src/views/falukant/OverviewView.vue @@ -0,0 +1,187 @@ + + + + + diff --git a/package-lock.json b/package-lock.json index 3d54ba1..93a892e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,6 @@ "version": "3.0.0-pre-alpha.0.1", "dependencies": { "cors": "^2.8.5", - "mitt": "^3.0.1", "sequelize-cli": "^6.6.2" }, "devDependencies": { @@ -483,11 +482,10 @@ } }, "node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, - "license": "MIT", "dependencies": { "nice-try": "^1.0.4", "path-key": "^2.0.1", @@ -1830,12 +1828,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "license": "MIT" - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",