This commit is contained in:
rgrgogu
2026-05-05 23:18:47 +08:00
parent 5aeb959e92
commit a8f10a25d7
38 changed files with 5992 additions and 2 deletions
+22
View File
@@ -0,0 +1,22 @@
const excludeAttributes = [
// Add here
];
const jsonbSchemas = {
// Add here
};
// Different exclude sets per role
const adminExclude = [
...excludeAttributes,
// admins can see audit fields, so nothing extra excluded
];
const userExclude = [
...excludeAttributes,
// regular users cannot see audit trails
"created_by", "updated_by", "deleted_by",
"deleted_at",
];
module.exports = { excludeAttributes, adminExclude, userExclude, jsonbSchemas };
+70
View File
@@ -0,0 +1,70 @@
/***********************************************************************************************************************************************************************
* File Name: user_groups.mdl.js
* Type of Program: Model
* Description: Sequelize models for `user_groups` and `user_group_members` tables.
* Implements a Many-to-Many self-contained group system.
* Groups are used as named permission bundles.
* Author: rgrgogu
* Date Created: Oct. 6, 2025
***********************************************************************************************************************************************************************
* HOW TO USE:
* const { mdl_UserGroups, mdl_UserGroupMembers } = require('./models/user_groups.mdl');
***********************************************************************************************************************************************************************/
const { DataTypes } = require('sequelize');
const sequelize = require('../../config/db.config');
const mdl_Users = require('./users.mdl');
// ─── UserGroups ────────────────────────────────────────────────────────────────
const mdl_UserGroups = sequelize.define('UserGroup', {
group_id: { type: DataTypes.BIGINT, primaryKey: true, autoIncrement: true },
name: { type: DataTypes.STRING(50), allowNull: false },
description: { type: DataTypes.TEXT },
is_active: { type: DataTypes.BOOLEAN, defaultValue: true },
// ── Audit trails ────────────────────────────────────────────────────────────
createdBy: { type: DataTypes.BIGINT, allowNull: true, label: "Created By" },
updatedBy: { type: DataTypes.BIGINT, allowNull: true, label: "Modified By" },
deletedBy: { type: DataTypes.BIGINT, allowNull: true, label: "Deleted By" },
}, {
tableName: 'user_groups',
timestamps: true,
paranoid: true,
});
// ─── Junction: UserGroupMembers ────────────────────────────────────────────────
const mdl_UserGroupMembers = sequelize.define('UserGroupMember', {
group_id: { type: DataTypes.BIGINT, primaryKey: true },
user_id: { type: DataTypes.BIGINT, primaryKey: true },
joined_at: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
// ── Audit trails ────────────────────────────────────────────────────────────
createdBy: { type: DataTypes.BIGINT, allowNull: true, label: "Created By" },
updatedBy: { type: DataTypes.BIGINT, allowNull: true, label: "Modified By" },
deletedBy: { type: DataTypes.BIGINT, allowNull: true, label: "Deleted By" },
}, {
tableName: 'user_group_members',
timestamps: true,
paranoid: true,
});
// ─── Associations ──────────────────────────────────────────────────────────────
mdl_Users.belongsToMany(mdl_UserGroups, { through: mdl_UserGroupMembers, foreignKey: 'user_id', otherKey: 'group_id', as: 'groups' });
mdl_UserGroups.belongsToMany(mdl_Users, { through: mdl_UserGroupMembers, foreignKey: 'group_id', otherKey: 'user_id', as: 'members', });
mdl_UserGroups.hasMany(mdl_UserGroupMembers, { foreignKey: 'group_id' });
mdl_UserGroupMembers.belongsTo(mdl_UserGroups, { foreignKey: 'group_id' });
mdl_Users.hasMany(mdl_UserGroupMembers, { foreignKey: 'user_id' });
mdl_UserGroupMembers.belongsTo(mdl_Users, { foreignKey: 'user_id' });
// models/users/user_groups.mdl.js — add at the bottom
mdl_UserGroups.belongsTo(mdl_Users, { as: 'creator', foreignKey: 'createdBy' });
mdl_UserGroups.belongsTo(mdl_Users, { as: 'modifier', foreignKey: 'updatedBy' });
mdl_UserGroups.belongsTo(mdl_Users, { as: 'deleter', foreignKey: 'deletedBy' });
// Self-referencing associations for audit fields
mdl_Users.belongsTo(mdl_Users, { as: 'creator', foreignKey: 'createdBy' });
mdl_Users.belongsTo(mdl_Users, { as: 'modifier', foreignKey: 'updatedBy' });
mdl_Users.belongsTo(mdl_Users, { as: 'deleter', foreignKey: 'deletedBy' });
module.exports = { mdl_UserGroups, mdl_UserGroupMembers };
+60
View File
@@ -0,0 +1,60 @@
/***********************************************************************************************************************************************************************
* File Name: user_sessions.mdl.js
* Type of Program: Model
* Description: Sequelize model for the `user_sessions` table.
* Captures login and logout audit data (IP, geo, device) as JSONB.
* Has a Many-to-One relationship with Users.
* Author: rgrgogu
* Date Created: Oct. 6, 2025
***********************************************************************************************************************************************************************
* Change History:
* DATE AUTHOR LOG DESCRIPTION
* Oct. 6, 2025 rgrgogu 001 Initial creation - STAR Phase 1
***********************************************************************************************************************************************************************/
const { DataTypes } = require('sequelize');
const sequelize = require('../../config/db.config');
const mdl_Users = require('./users.mdl');
const mdl_UserSessions = sequelize.define('UserSessions', {
session_id: { type: DataTypes.BIGINT, primaryKey: true, autoIncrement: true },
user_id: {
type: DataTypes.BIGINT,
allowNull: false,
references: { model: mdl_Users, key: 'user_id' },
},
/**
* login_info / logout_info JSONB:
* {
* date: ISO string,
* ip_address: string,
* country: string,
* region: string,
* city: string,
* lat: number,
* long: number,
* device_info: { ua, browser, os, device }
* forced_by: user_id
* }
*/
login_info: { type: DataTypes.JSONB, allowNull: true },
logout_info: { type: DataTypes.JSONB, allowNull: true },
// Store the refresh-token hash so we can invalidate individual sessions
refresh_token_hash: { type: DataTypes.TEXT, allowNull: true },
is_active: { type: DataTypes.BOOLEAN, defaultValue: true },
// ── Audit trails ────────────────────────────────────────────────────────────
createdBy: { type: DataTypes.BIGINT, allowNull: true, label: "Created By" },
updatedBy: { type: DataTypes.BIGINT, allowNull: true, label: "Modified By" },
deletedBy: { type: DataTypes.BIGINT, allowNull: true, label: "Deleted By" },
}, {
tableName: 'user_sessions',
timestamps: true,
paranoid: true, // enables soft delete — sets deleted_at instead of DELETE
});
// Associations
mdl_UserSessions.belongsTo(mdl_Users, { foreignKey: 'user_id' });
mdl_Users.hasMany(mdl_UserSessions, { foreignKey: 'user_id' });
module.exports = mdl_UserSessions;
+47
View File
@@ -0,0 +1,47 @@
const excludeAttributes = [
"password", "otp_code", "otp_expires_at",
"personal_info.name.given_name",
"personal_info.name.middle_name",
"personal_info.name.last_name",
"personal_info.name.extension_name",
"personal_info.addresses[].city",
"personal_info.addresses[].country",
"personal_info.addresses[].street",
"personal_info.addresses[].zip",
"personal_info.addresses[].address_type",
"personal_info.addresses[].state",
"personal_info.phone_number[].country_code",
"personal_info.phone_number[].number",
"personal_info.phone_number[].phone_type",
];
const jsonbSchemas = {
personal_info: {
name: {
full_name: { type: "text", label: "Full Name" },
},
date_of_birth: { type: "date", label: "Date of Birth" },
occupation: { type: "text", label: "Occupation" },
addresses: {
full_address: { type: "text", label: "Full Address" },
},
phone_number: {
full_number: { type: "text", label: "Phone Number" },
},
},
};
// Different exclude sets per role
const adminExclude = [
...excludeAttributes,
// admins can see audit fields, so nothing extra excluded
];
const userExclude = [
...excludeAttributes,
// regular users cannot see audit trails
"created_by", "updated_by", "deleted_by",
"deleted_at",
];
module.exports = { excludeAttributes, adminExclude, userExclude, jsonbSchemas };
+59
View File
@@ -0,0 +1,59 @@
/***********************************************************************************************************************************************************************
* File Name: users.mdl.js
* Type of Program: Model
* Description: Sequelize model for the `users` table.
* Stores authentication credentials, account flags, registration type,
* role/account type, and a flexible JSONB personal_info column.
* Author: rgrgogu
* Date Created: Oct. 6, 2025
***********************************************************************************************************************************************************************
* Change History:
* DATE AUTHOR LOG DESCRIPTION
* Oct. 6, 2025 rgrgogu 001 Initial creation - STAR Phase 1
***********************************************************************************************************************************************************************/
const { DataTypes } = require('sequelize');
const sequelize = require('../../config/db.config');
const mdl_Users = sequelize.define('User', {
user_id: { type: DataTypes.BIGINT, primaryKey: true, autoIncrement: true, label: "User ID" },
email: { type: DataTypes.STRING(255), allowNull: false, unique: true, label: "Email Address", validate: { isEmail: true } },
password: { type: DataTypes.TEXT, label: "Password" },
is_active: { type: DataTypes.BOOLEAN, defaultValue: true, label: "Active" },
is_verified: { type: DataTypes.BOOLEAN, defaultValue: false, label: "Verified" },
reg_type: { type: DataTypes.ENUM('system', 'google'), defaultValue: 'system', label: "Registration Type" },
/**
* acc_type drives RBAC:
* - "user" → Client endpoints only
* - "staff" → Client + Staff endpoints
* - "admin" → All endpoints
*/
acc_type: { type: DataTypes.ENUM('user', 'staff', 'admin'), defaultValue: 'user', label: "Account Type" },
/**
* personal_info JSONB structure:
* {
* name: { given_name, middle_name, last_name, extension_name, full_name },
* occupation: string,
* addresses: [{ street, city, state, zip, country, address_type, full_address }],
* phone_number: [{ number, country_code, phone_type, full_number }],
* date_of_birth: date,
* avatar: { mime_type, name, size, url, uuid },
* album: [{ uuid, file_url, original_name, uploaded_at, order_index }]
* }
*/
personal_info: { type: DataTypes.JSONB, defaultValue: null, label: "Personal Info" },
// OTP fields (stored temporarily during verification flow)
otp_code: { type: DataTypes.STRING(6), allowNull: true, label: "OTP Code" },
otp_expires_at: { type: DataTypes.DATE, allowNull: true, label: "OTP Expires At" },
// ── Audit trails ────────────────────────────────────────────────────────────
createdBy: { type: DataTypes.BIGINT, allowNull: true, label: "Created By" },
updatedBy: { type: DataTypes.BIGINT, allowNull: true, label: "Modified By" },
deletedBy: { type: DataTypes.BIGINT, allowNull: true, label: "Deleted By" },
}, {
tableName: 'users',
timestamps: true,
paranoid: true, // enables soft delete — sets deleted_at instead of DELETE
});
module.exports = mdl_Users;