@maxisoft/instantcms-mcp 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +308 -0
  3. package/dist/__tests__/artifact-tool.test.js +196 -0
  4. package/dist/__tests__/db-tool.test.js +152 -0
  5. package/dist/__tests__/define-tool.test.js +106 -0
  6. package/dist/__tests__/find-tool.test.js +185 -0
  7. package/dist/__tests__/get-component-api.test.js +144 -0
  8. package/dist/__tests__/hardening.test.js +73 -0
  9. package/dist/__tests__/hooks-tool.test.js +173 -0
  10. package/dist/__tests__/mcp-integration-extra.test.js +166 -0
  11. package/dist/__tests__/mcp-integration.test.js +53 -0
  12. package/dist/__tests__/pagination.test.js +114 -0
  13. package/dist/__tests__/performance-baseline.test.js +56 -0
  14. package/dist/__tests__/project-patch.test.js +144 -0
  15. package/dist/__tests__/project-source.test.js +44 -0
  16. package/dist/__tests__/project-workflows-extra.test.js +165 -0
  17. package/dist/__tests__/project-workflows.test.js +51 -0
  18. package/dist/__tests__/scaffold-addon-roundtrip.test.js +287 -0
  19. package/dist/__tests__/serialization.test.js +170 -0
  20. package/dist/__tests__/source-knowledge-parsers.test.js +42 -0
  21. package/dist/__tests__/template-development.test.js +78 -0
  22. package/dist/__tests__/template-productivity.test.js +87 -0
  23. package/dist/__tests__/tools.test.js +2143 -0
  24. package/dist/data/components.js +2818 -0
  25. package/dist/data/controllers-map.js +7571 -0
  26. package/dist/data/core-api.js +9356 -0
  27. package/dist/data/database-schema.js +4555 -0
  28. package/dist/data/events-map.js +1339 -0
  29. package/dist/data/fields-map.js +3692 -0
  30. package/dist/data/hooks.js +2257 -0
  31. package/dist/data/js-api.js +123 -0
  32. package/dist/data/libs-api.js +264 -0
  33. package/dist/data/routes-map.js +203 -0
  34. package/dist/data/schemas-validation.js +255 -0
  35. package/dist/data/schemas.js +1404 -0
  36. package/dist/data/traits-map.js +1004 -0
  37. package/dist/data/version-profiles.js +41 -0
  38. package/dist/data/widgets-map.js +131 -0
  39. package/dist/data/wysiwyg-map.js +486 -0
  40. package/dist/generated/components-source.js +14748 -0
  41. package/dist/generated/hooks-source.js +3399 -0
  42. package/dist/generated/knowledge-meta.js +101 -0
  43. package/dist/index.js +13 -0
  44. package/dist/registry/database-tools.js +112 -0
  45. package/dist/registry/extension-tools.js +473 -0
  46. package/dist/registry/generator-tools.js +506 -0
  47. package/dist/registry/knowledge-tools.js +441 -0
  48. package/dist/registry/language-tools.js +99 -0
  49. package/dist/registry/meta-tools.js +152 -0
  50. package/dist/registry/project-tools.js +48 -0
  51. package/dist/registry/resources.js +98 -0
  52. package/dist/registry/source-tools.js +211 -0
  53. package/dist/registry/template-development-tools.js +79 -0
  54. package/dist/server.js +32 -0
  55. package/dist/tools/addon-tool.js +1437 -0
  56. package/dist/tools/admin-partial-tool.js +498 -0
  57. package/dist/tools/api-tool.js +294 -0
  58. package/dist/tools/artifact-tool.js +95 -0
  59. package/dist/tools/cache-tool.js +360 -0
  60. package/dist/tools/component-tool.js +415 -0
  61. package/dist/tools/controllers-tool.js +125 -0
  62. package/dist/tools/cron-tool.js +197 -0
  63. package/dist/tools/crud-tool.js +659 -0
  64. package/dist/tools/db-tool.js +198 -0
  65. package/dist/tools/email-tool.js +222 -0
  66. package/dist/tools/external-api-tool.js +596 -0
  67. package/dist/tools/filter-tool.js +341 -0
  68. package/dist/tools/form-tool.js +275 -0
  69. package/dist/tools/grid-tool.js +189 -0
  70. package/dist/tools/hooks-tool.js +104 -0
  71. package/dist/tools/import-export-tool.js +548 -0
  72. package/dist/tools/lang-tool.js +251 -0
  73. package/dist/tools/layout-override-tool.js +125 -0
  74. package/dist/tools/layout-tool.js +548 -0
  75. package/dist/tools/maria-tool.js +127 -0
  76. package/dist/tools/mariadb.js +201 -0
  77. package/dist/tools/migration-tool.js +329 -0
  78. package/dist/tools/oauth-tool.js +520 -0
  79. package/dist/tools/parser/components-parser.js +76 -0
  80. package/dist/tools/parser/controllers-parser.js +313 -0
  81. package/dist/tools/parser/core-parser.js +294 -0
  82. package/dist/tools/parser/coverage-generator.js +424 -0
  83. package/dist/tools/parser/events-parser.js +127 -0
  84. package/dist/tools/parser/fields-parser.js +382 -0
  85. package/dist/tools/parser/hooks-parser.js +106 -0
  86. package/dist/tools/parser/sql-parser.js +197 -0
  87. package/dist/tools/parser/traits-parser.js +161 -0
  88. package/dist/tools/parser/widgets-parser.js +150 -0
  89. package/dist/tools/permission-tool.js +348 -0
  90. package/dist/tools/project-patch-tool.js +73 -0
  91. package/dist/tools/project-source-tool.js +188 -0
  92. package/dist/tools/project-workflow-tool.js +186 -0
  93. package/dist/tools/requirement-tool.js +212 -0
  94. package/dist/tools/scaffold-tool.js +815 -0
  95. package/dist/tools/seo-tool.js +412 -0
  96. package/dist/tools/source-tool.js +155 -0
  97. package/dist/tools/template-development-tool.js +271 -0
  98. package/dist/tools/template-overrides-tool.js +294 -0
  99. package/dist/tools/template-productivity-tool.js +319 -0
  100. package/dist/tools/template-tool.js +665 -0
  101. package/dist/tools/test-tool.js +183 -0
  102. package/dist/tools/webhook-tool.js +427 -0
  103. package/dist/tools/widget-tool.js +331 -0
  104. package/dist/tools/wysiwyg-tool.js +137 -0
  105. package/dist/types/scaffold.js +68 -0
  106. package/dist/utils/define-tool.js +37 -0
  107. package/dist/utils/find-tool.js +97 -0
  108. package/dist/utils/mcp-result.js +18 -0
  109. package/dist/utils/pagination.js +27 -0
  110. package/dist/utils/serialization.js +27 -0
  111. package/package.json +94 -0
@@ -0,0 +1,201 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.getPool = getPool;
37
+ exports.executeQuery = executeQuery;
38
+ exports.getTableInfo = getTableInfo;
39
+ exports.listTables = listTables;
40
+ exports.getDatabaseInfo = getDatabaseInfo;
41
+ exports.describeTable = describeTable;
42
+ exports.showIndexes = showIndexes;
43
+ exports.closePool = closePool;
44
+ const mysql = __importStar(require("mysql2/promise"));
45
+ let pool = null;
46
+ function getConnection() {
47
+ const host = process.env.DB_HOST || 'localhost';
48
+ const port = parseInt(process.env.DB_PORT || '3306', 10);
49
+ const user = process.env.DB_USER || 'root';
50
+ const password = process.env.DB_PASSWORD || '';
51
+ const database = process.env.DB_DATABASE || 'instantcms';
52
+ return { host, port, user, password, database };
53
+ }
54
+ function getPool() {
55
+ if (!pool) {
56
+ const config = getConnection();
57
+ pool = mysql.createPool({
58
+ host: config.host,
59
+ port: config.port,
60
+ user: config.user,
61
+ password: config.password,
62
+ database: config.database,
63
+ waitForConnections: true,
64
+ connectionLimit: 5,
65
+ queueLimit: 0
66
+ });
67
+ }
68
+ return pool;
69
+ }
70
+ async function executeQuery(sql) {
71
+ const startTime = Date.now();
72
+ try {
73
+ const [rows, fields] = await getPool().execute(sql);
74
+ const executionTime = Date.now() - startTime;
75
+ const columns = Array.isArray(fields) ? fields.map((f) => f.name) : [];
76
+ const rowCount = Array.isArray(rows) ? rows.length : 0;
77
+ return {
78
+ columns,
79
+ rows: Array.isArray(rows) ? rows : [],
80
+ rowCount,
81
+ query: sql,
82
+ executionTime
83
+ };
84
+ }
85
+ catch (error) {
86
+ const executionTime = Date.now() - startTime;
87
+ return {
88
+ columns: [],
89
+ rows: [],
90
+ rowCount: 0,
91
+ query: sql,
92
+ executionTime,
93
+ error: error instanceof Error ? error.message : String(error)
94
+ };
95
+ }
96
+ }
97
+ async function getTableInfo(tableName) {
98
+ const [columnsResult, indexesResult, countResult] = await Promise.all([
99
+ executeQuery(`
100
+ SELECT
101
+ COLUMN_NAME as name,
102
+ COLUMN_TYPE as type,
103
+ IS_NULLABLE as nullable,
104
+ COLUMN_KEY as \`key\`,
105
+ COLUMN_DEFAULT as \`default\`,
106
+ EXTRA as extra,
107
+ COLUMN_COMMENT as comment
108
+ FROM information_schema.COLUMNS
109
+ WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?
110
+ `),
111
+ executeQuery(`
112
+ SELECT
113
+ INDEX_NAME as name,
114
+ INDEX_TYPE as type,
115
+ GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX) as columns,
116
+ NON_UNIQUE as non_unique
117
+ FROM information_schema.STATISTICS
118
+ WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?
119
+ GROUP BY INDEX_NAME, INDEX_TYPE, NON_UNIQUE
120
+ `),
121
+ executeQuery(`SELECT COUNT(*) as cnt FROM \`${tableName}\``)
122
+ ]);
123
+ if (columnsResult.error || columnsResult.rows.length === 0) {
124
+ return null;
125
+ }
126
+ return {
127
+ name: tableName,
128
+ comment: null,
129
+ columns: columnsResult.rows.map(row => ({
130
+ name: String(row.name),
131
+ type: String(row.type),
132
+ nullable: row.nullable === 'YES',
133
+ key: row.key ? String(row.key) : null,
134
+ default: row.default,
135
+ extra: row.extra,
136
+ comment: row.comment
137
+ })),
138
+ indexes: indexesResult.rows.map(row => ({
139
+ name: String(row.name),
140
+ type: row.type ? String(row.type) : null,
141
+ columns: String(row.columns).split(','),
142
+ unique: row.non_unique === 0
143
+ })),
144
+ rowCount: countResult.rows[0]?.cnt
145
+ };
146
+ }
147
+ async function listTables() {
148
+ const result = await executeQuery(`
149
+ SELECT TABLE_NAME
150
+ FROM information_schema.TABLES
151
+ WHERE TABLE_SCHEMA = DATABASE()
152
+ AND TABLE_TYPE = 'BASE TABLE'
153
+ ORDER BY TABLE_NAME
154
+ `);
155
+ return result.rows.map(row => String(row.TABLE_NAME));
156
+ }
157
+ async function getDatabaseInfo() {
158
+ const result = await executeQuery(`
159
+ SELECT
160
+ TABLE_NAME,
161
+ TABLE_ROWS,
162
+ ROUND(DATA_LENGTH / 1024 / 1024, 2) as data_mb,
163
+ ROUND(INDEX_LENGTH / 1024 / 1024, 2) as index_mb
164
+ FROM information_schema.TABLES
165
+ WHERE TABLE_SCHEMA = DATABASE()
166
+ AND TABLE_TYPE = 'BASE TABLE'
167
+ `);
168
+ const tablesCount = result.rows.length;
169
+ let totalRows = 0;
170
+ let totalData = 0;
171
+ let totalIndex = 0;
172
+ for (const row of result.rows) {
173
+ totalRows += Number(row.TABLE_ROWS) || 0;
174
+ totalData += Number(row.data_mb) || 0;
175
+ totalIndex += Number(row.index_mb) || 0;
176
+ }
177
+ const dbResult = await executeQuery('SELECT DATABASE() as db_name');
178
+ const dbName = String(dbResult.rows[0]?.db_name || 'unknown');
179
+ return {
180
+ name: dbName,
181
+ tablesCount,
182
+ totalRows,
183
+ size: {
184
+ data: totalData,
185
+ index: totalIndex,
186
+ total: totalData + totalIndex
187
+ }
188
+ };
189
+ }
190
+ async function describeTable(tableName) {
191
+ return executeQuery(`DESCRIBE \`${tableName}\``);
192
+ }
193
+ async function showIndexes(tableName) {
194
+ return executeQuery(`SHOW INDEX FROM \`${tableName}\``);
195
+ }
196
+ async function closePool() {
197
+ if (pool) {
198
+ await pool.end();
199
+ pool = null;
200
+ }
201
+ }
@@ -0,0 +1,329 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateMigration = generateMigration;
4
+ exports.scaffoldMigration = scaffoldMigration;
5
+ exports.generateFieldSuggestions = generateFieldSuggestions;
6
+ function generateMigration(name, fields, options) {
7
+ const tableName = `cms_${name}`;
8
+ const className = name
9
+ .split('_')
10
+ .map(s => s.charAt(0).toUpperCase() + s.slice(1))
11
+ .join('');
12
+ const fieldLines = fields.map(f => {
13
+ let line = ` \`${f.name}\` ${f.type}`;
14
+ if (f.nullable !== true) {
15
+ line += ' NOT NULL';
16
+ }
17
+ if (f.default !== undefined) {
18
+ if (f.default === 'NULL') {
19
+ line += ' DEFAULT NULL';
20
+ }
21
+ else if (typeof f.default === 'number') {
22
+ line += ` DEFAULT ${f.default}`;
23
+ }
24
+ else {
25
+ line += ` DEFAULT '${f.default}'`;
26
+ }
27
+ }
28
+ if (f.extra) {
29
+ line += ` ${f.extra}`;
30
+ }
31
+ if (f.comment) {
32
+ line += ` COMMENT '${f.comment}'`;
33
+ }
34
+ return line;
35
+ });
36
+ if (!fields.find(f => f.key === 'PRI')) {
37
+ fieldLines.push(' PRIMARY KEY (`id`)');
38
+ }
39
+ const indexes = [];
40
+ const seenKeys = new Set();
41
+ for (const f of fields) {
42
+ if (f.key && f.key !== 'PRI' && !seenKeys.has(f.key + '_' + f.name)) {
43
+ seenKeys.add(f.key + '_' + f.name);
44
+ if (f.key === 'UNI') {
45
+ indexes.push(` UNIQUE KEY \`${f.name}\` (\`${f.name}\`)`);
46
+ }
47
+ else if (f.key === 'MUL') {
48
+ indexes.push(` KEY \`${f.name}\` (\`${f.name}\`)`);
49
+ }
50
+ }
51
+ }
52
+ if (options?.indexes) {
53
+ for (const idx of options.indexes) {
54
+ if (idx.type === 'UNIQUE') {
55
+ indexes.push(` UNIQUE KEY \`${idx.name}\` (\`${idx.fields.join('`, `')}\`)`);
56
+ }
57
+ else if (idx.type === 'FULLTEXT') {
58
+ indexes.push(` FULLTEXT KEY \`${idx.name}\` (\`${idx.fields.join('`, `')}\`)`);
59
+ }
60
+ else {
61
+ indexes.push(` KEY \`${idx.name}\` (\`${idx.fields.join('`, `')}\`)`);
62
+ }
63
+ }
64
+ }
65
+ const allLines = [...fieldLines, ...indexes];
66
+ const createTable = `CREATE TABLE \`${tableName}\` (
67
+ ${allLines.join(',\n')}
68
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8${options?.comment ? ` COMMENT='${options.comment}'` : ''};`;
69
+ const installCode = `class installer${className} extends cmsInstaller {
70
+
71
+ public function install() {
72
+
73
+ $this->createTable('${name}', [
74
+ ${fields
75
+ .map(f => {
76
+ const opts = [];
77
+ if (f.nullable)
78
+ opts.push("'null' => true");
79
+ if (f.default !== undefined)
80
+ opts.push(`'default' => '${f.default}'"`);
81
+ if (f.extra === 'AUTO_INCREMENT')
82
+ opts.push("'auto_increment' => true");
83
+ if (f.comment)
84
+ opts.push(`'comment' => '${f.comment}'"`);
85
+ return ` '${f.name}' => ['${f.type}'${opts.length > 0 ? ', ' + opts.join(', ') : ''}]`;
86
+ })
87
+ .join(',\n')}
88
+ ]${options?.comment ? `, '${options.comment}'` : ''});
89
+
90
+ return true;
91
+ }
92
+
93
+ public function uninstall() {
94
+ $this->dropTable('${name}');
95
+ return true;
96
+ }
97
+ }`;
98
+ return {
99
+ table_name: tableName,
100
+ class_name: `installer${className}`,
101
+ sql: createTable,
102
+ install_php: installCode,
103
+ field_count: fields.length,
104
+ index_count: indexes.length,
105
+ conventions: {
106
+ table_name: `cms_${name}`,
107
+ model_class: `model${className}`,
108
+ controller_class: name,
109
+ manifest_example: `<files>
110
+ <file>system/controllers/${name}/</file>
111
+ </files>
112
+ <hooks>
113
+ <hook event="content_after_add_approve" />
114
+ </hooks>`,
115
+ },
116
+ };
117
+ }
118
+ function scaffoldMigration(params) {
119
+ const { addon_name, table_name, fields, options = {} } = params;
120
+ const tableName = `cms_${table_name}`;
121
+ const className = addon_name
122
+ .split('_')
123
+ .map(s => s.charAt(0).toUpperCase() + s.slice(1))
124
+ .join('');
125
+ const installerClass = `installer${className}`;
126
+ // install.php
127
+ const installContent = `<?php
128
+ /**
129
+ * Установка дополнения ${addon_name}
130
+ */
131
+
132
+ class ${installerClass} extends cmsInstaller {
133
+
134
+ public function install() {
135
+
136
+ $this->addModule('${addon_name}', [
137
+ 'title' => '${addon_name.toUpperCase()}',
138
+ 'description' => '',
139
+ 'version' => '1.0.0',
140
+ 'author' => '',
141
+ 'url' => ''
142
+ ]);
143
+
144
+ // Создание таблицы
145
+ $this->createTable('${table_name}', [
146
+ ${fields
147
+ .map(f => {
148
+ const opts = [];
149
+ if (f.nullable)
150
+ opts.push("'null' => true");
151
+ if (f.default !== undefined && f.default !== 'NULL') {
152
+ const isNumber = typeof f.default === 'number' || /^\d+$/.test(String(f.default));
153
+ opts.push(`'default' => ${isNumber ? f.default : `'${f.default}'`}`);
154
+ }
155
+ else if (f.default === 'NULL') {
156
+ opts.push("'null' => true");
157
+ }
158
+ if (f.extra === 'AUTO_INCREMENT')
159
+ opts.push("'auto_increment' => true");
160
+ if (f.comment)
161
+ opts.push(`'comment' => '${f.comment}'"`);
162
+ return ` '${f.name}' => ['${f.type}'${opts.length > 0 ? ', ' + opts.join(', ') : ''}]`;
163
+ })
164
+ .join(',\n')}
165
+ ]${options.comment ? `, '${options.comment}'` : ''});${options.indexes?.length
166
+ ? `
167
+ // Индексы
168
+ ${options.indexes
169
+ .map(idx => {
170
+ let sqlType = 'INDEX';
171
+ if (idx.type === 'UNIQUE') {
172
+ sqlType = 'UNIQUE INDEX';
173
+ }
174
+ else if (idx.type === 'FULLTEXT') {
175
+ sqlType = 'FULLTEXT INDEX';
176
+ }
177
+ const sql = `CREATE ${sqlType} ${idx.name} ON {${table_name}} (${idx.fields.join(', ')})`;
178
+ return ` \\$this->db->query("${sql}");`;
179
+ })
180
+ .join('\n')}`
181
+ : ''}${options.content_type
182
+ ? `
183
+
184
+ // Регистрация типа контента
185
+ $this->registerContentType('${addon_name}', [
186
+ 'title' => '${className}',
187
+ 'description' => '',
188
+ 'options' => ['flags' => ['is_fixed_url']],
189
+ 'item_url_mask' => '/${addon_name}/view/{id}',
190
+ 'templates' => [
191
+ 'list' => '${addon_name}_list',
192
+ 'item' => '${addon_name}_item',
193
+ 'profile'=> ''
194
+ ]
195
+ ]);`
196
+ : ''}${options.has_seo
197
+ ? `
198
+
199
+ // SEO настройки
200
+ $this->addSeoOptions('${addon_name}', [
201
+ 'slug_default' => '${addon_name}',
202
+ 'meta' => [
203
+ 'title' => '{title}',
204
+ 'description' => '{description}',
205
+ 'keywords' => ''
206
+ ]
207
+ ]);`
208
+ : ''}
209
+
210
+ return true;
211
+ }
212
+
213
+ public function uninstall($hard = false) {
214
+
215
+ if ($hard) {
216
+ $this->dropTable('${table_name}');${options.content_type
217
+ ? `
218
+ $this->deleteContentType('${addon_name}');`
219
+ : ''}
220
+ }
221
+
222
+ $this->removeModule('${addon_name}');
223
+
224
+ return true;
225
+ }
226
+ }`;
227
+ // uninstall.php
228
+ const uninstallContent = `<?php
229
+ /**
230
+ * Удаление дополнения ${addon_name}
231
+ */
232
+
233
+ class ${installerClass.replace('installer', 'uninstall_') + installerClass.includes('installer') ? '' : 'uninstall_'} extends ${installerClass} {
234
+
235
+ public function uninstall() {
236
+ parent::uninstall(false);
237
+ }
238
+ }`;
239
+ // manifest.xml entries для install
240
+ const manifestEntry = `<files>
241
+ <file>system/controllers/${addon_name}/install.php</file>
242
+ </files>`;
243
+ return {
244
+ addon_name,
245
+ table_name: tableName,
246
+ files: {
247
+ 'install.php': installContent,
248
+ 'uninstall.php': uninstallContent,
249
+ },
250
+ manifest_xml: manifestEntry,
251
+ notes: {
252
+ 'install.php': `Файл должен быть в: system/controllers/${addon_name}/install.php`,
253
+ 'uninstall.php': `Файл должен быть в: system/controllers/${addon_name}/uninstall.php`,
254
+ hard_uninstall: 'parent::uninstall(true) удалит таблицы, parent::uninstall(false) только отключит модуль',
255
+ },
256
+ };
257
+ }
258
+ function generateFieldSuggestions(fieldType) {
259
+ const suggestions = {
260
+ string: [
261
+ { name: 'title', type: 'varchar(255)', nullable: false, comment: 'Заголовок' },
262
+ { name: 'slug', type: 'varchar(100)', nullable: true, comment: 'URL-псевдоним' },
263
+ { name: 'email', type: 'varchar(100)', nullable: true, comment: 'Email' },
264
+ { name: 'phone', type: 'varchar(20)', nullable: true, comment: 'Телефон' },
265
+ ],
266
+ text: [
267
+ { name: 'description', type: 'text', nullable: true, comment: 'Описание' },
268
+ { name: 'content', type: 'text', nullable: true, comment: 'Контент' },
269
+ { name: 'content_short', type: 'text', nullable: true, comment: 'Краткое описание' },
270
+ ],
271
+ number: [
272
+ { name: 'price', type: 'decimal(10,2)', nullable: true, comment: 'Цена' },
273
+ { name: 'quantity', type: 'int(11)', nullable: true, default: '0', comment: 'Количество' },
274
+ { name: 'sorting', type: 'int(11)', nullable: true, default: '0', comment: 'Сортировка' },
275
+ ],
276
+ datetime: [
277
+ { name: 'date_pub', type: 'datetime', nullable: true, comment: 'Дата публикации' },
278
+ { name: 'date_end', type: 'datetime', nullable: true, comment: 'Дата окончания' },
279
+ {
280
+ name: 'created_at',
281
+ type: 'timestamp',
282
+ nullable: false,
283
+ default: 'CURRENT_TIMESTAMP',
284
+ comment: 'Создано',
285
+ },
286
+ ],
287
+ user: [
288
+ {
289
+ name: 'user_id',
290
+ type: 'int(11) UNSIGNED',
291
+ nullable: true,
292
+ key: 'MUL',
293
+ comment: 'ID пользователя',
294
+ },
295
+ ],
296
+ bool: [
297
+ {
298
+ name: 'is_pub',
299
+ type: 'tinyint(1) UNSIGNED',
300
+ nullable: false,
301
+ default: '1',
302
+ comment: 'Опубликовано',
303
+ },
304
+ {
305
+ name: 'is_deleted',
306
+ type: 'tinyint(1) UNSIGNED',
307
+ nullable: false,
308
+ default: '0',
309
+ comment: 'Удалено',
310
+ },
311
+ {
312
+ name: 'is_featured',
313
+ type: 'tinyint(1) UNSIGNED',
314
+ nullable: false,
315
+ default: '0',
316
+ comment: 'Избранное',
317
+ },
318
+ ],
319
+ };
320
+ return (suggestions[fieldType] || [
321
+ {
322
+ name: 'id',
323
+ type: 'int(11) UNSIGNED',
324
+ nullable: false,
325
+ extra: 'AUTO_INCREMENT',
326
+ key: 'PRI',
327
+ },
328
+ ]);
329
+ }