Software: Apache. PHP/8.1.30 uname -a: Linux server1.tuhinhossain.com 5.15.0-163-generic #173-Ubuntu SMP Tue Oct 14 17:51:00 UTC uid=1002(picotech) gid=1003(picotech) groups=1003(picotech),0(root) Safe-mode: OFF (not secure) /home/picotech/domains/note.picotech.app/public_html/src/models/ drwxr-xr-x | |
| Viewing file: Select action/file-type: const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/database');
const Note = sequelize.define('Note', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
meeting_id: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'meetings',
key: 'id',
},
onDelete: 'CASCADE',
},
user_id: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'users',
key: 'id',
},
onDelete: 'CASCADE',
},
title: {
type: DataTypes.STRING(255),
allowNull: true,
defaultValue: 'Untitled Note',
},
content_blocks: {
type: DataTypes.JSON,
allowNull: false,
defaultValue: [],
validate: {
isValidContentBlocks(value) {
if (!Array.isArray(value)) {
throw new Error('Content blocks must be an array');
}
for (const block of value) {
if (!block.id || !block.type || typeof block.content !== 'string') {
throw new Error('Each content block must have id, type, and content');
}
if (!['text', 'bullet', 'heading', 'action_item', 'ai_suggestion'].includes(block.type)) {
throw new Error('Invalid content block type');
}
}
},
},
},
tags: {
type: DataTypes.JSON,
defaultValue: [],
allowNull: true,
validate: {
isArray(value) {
if (!Array.isArray(value)) {
throw new Error('Tags must be an array');
}
},
},
},
status: {
type: DataTypes.ENUM('draft', 'active', 'completed', 'archived'),
defaultValue: 'draft',
allowNull: false,
},
priority: {
type: DataTypes.ENUM('low', 'medium', 'high', 'urgent'),
defaultValue: 'medium',
allowNull: false,
},
color: {
type: DataTypes.STRING(7),
allowNull: true,
validate: {
is: /^#[0-9A-F]{6}$/i,
},
},
is_pinned: {
type: DataTypes.BOOLEAN,
defaultValue: false,
},
word_count: {
type: DataTypes.INTEGER,
defaultValue: 0,
allowNull: false,
},
last_edited_by: {
type: DataTypes.INTEGER,
allowNull: true,
references: {
model: 'users',
key: 'id',
},
},
version: {
type: DataTypes.INTEGER,
defaultValue: 1,
allowNull: false,
},
parent_note_id: {
type: DataTypes.INTEGER,
allowNull: true,
references: {
model: 'notes',
key: 'id',
},
comment: 'For note threading/replies',
},
}, {
tableName: 'notes',
timestamps: true,
indexes: [
{
fields: ['meeting_id'],
},
{
fields: ['user_id'],
},
{
fields: ['status'],
},
{
fields: ['meeting_id', 'user_id'],
},
{
fields: ['is_pinned'],
},
{
fields: ['priority'],
},
],
hooks: {
beforeSave: (note) => {
// Calculate word count from content blocks
if (note.content_blocks && Array.isArray(note.content_blocks)) {
const totalWords = note.content_blocks.reduce((count, block) => {
if (block.content && typeof block.content === 'string') {
return count + block.content.trim().split(/\s+/).filter(word => word.length > 0).length;
}
return count;
}, 0);
note.word_count = totalWords;
}
// Increment version on update
if (!note.isNewRecord) {
note.version += 1;
}
},
},
});
// Instance methods
Note.prototype.getPlainTextContent = function() {
if (!this.content_blocks || !Array.isArray(this.content_blocks)) {
return '';
}
return this.content_blocks
.map(block => block.content || '')
.join(' ')
.replace(/<[^>]*>/g, '') // Remove HTML tags
.trim();
};
Note.prototype.getActionItems = function() {
if (!this.content_blocks || !Array.isArray(this.content_blocks)) {
return [];
}
return this.content_blocks.filter(block => block.type === 'action_item');
};
Note.prototype.hasTag = function(tag) {
return this.tags && Array.isArray(this.tags) && this.tags.includes(tag);
};
Note.prototype.addTag = function(tag) {
if (!this.tags) {
this.tags = [];
}
if (!this.hasTag(tag)) {
this.tags.push(tag);
}
return this;
};
Note.prototype.removeTag = function(tag) {
if (this.tags && Array.isArray(this.tags)) {
this.tags = this.tags.filter(t => t !== tag);
}
return this;
};
// Class methods
Note.findByMeeting = function(meetingId, options = {}) {
return this.findAll({
where: {
meeting_id: meetingId,
...options.where,
},
order: options.order || [['created_at', 'ASC']],
...options,
});
};
Note.findByUser = function(userId, options = {}) {
return this.findAll({
where: {
user_id: userId,
...options.where,
},
order: options.order || [['updated_at', 'DESC']],
...options,
});
};
Note.findPinnedByUser = function(userId) {
return this.findAll({
where: {
user_id: userId,
is_pinned: true,
},
order: [['updated_at', 'DESC']],
});
};
Note.searchByContent = function(userId, searchTerm, options = {}) {
const { Op } = sequelize.Sequelize;
return this.findAll({
where: {
user_id: userId,
[Op.or]: [
{
title: {
[Op.like]: `%${searchTerm}%`,
},
},
sequelize.literal(`JSON_SEARCH(content_blocks, 'all', '%${searchTerm}%') IS NOT NULL`),
],
...options.where,
},
order: options.order || [['updated_at', 'DESC']],
limit: options.limit || 50,
});
};
module.exports = Note; |
:: Command execute :: | |
--[ c99shell v. 2.5 [PHP 8 Update] [24.05.2025] | Generation time: 0.0058 ]-- |