Compare commits
85 Commits
04c94167f7
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 1f52e74754 | |||
| 080eda2482 | |||
| 70309d2b21 | |||
| 6028b36f14 | |||
| 46f5b022fe | |||
| 2eccd21884 | |||
| 71b02a5ec8 | |||
| 78c4c851e9 | |||
| 1ad8968f56 | |||
| 22eda53d79 | |||
| 3b597aad7d | |||
| be68b1a239 | |||
| 1b3ccc6f35 | |||
| 8d741e418f | |||
| f1acef4dab | |||
| de388b14ea | |||
| 5db8346292 | |||
| 3a86aa4d6a | |||
| a38ed7a5a7 | |||
| cd69326e89 | |||
| c2b9ea9dcc | |||
| 95ac50594e | |||
| c25c6fd615 | |||
| 4282b925f7 | |||
| 155497d932 | |||
| b28230288c | |||
| ad3d377e33 | |||
| b28ffe377c | |||
| cb9a9874ab | |||
| f5e9ea6fd7 | |||
| e93882817d | |||
| 2db9d82c59 | |||
| 95227e0f42 | |||
| 1f2b4711ab | |||
| a572bad302 | |||
| a88369d800 | |||
| 03a58cda27 | |||
| 0130b2980a | |||
| 3a507fa69b | |||
| bafed635c2 | |||
| f67061a06a | |||
| e3adae23df | |||
| 4c394b3f6b | |||
| 101cc6141f | |||
| 4182fab2cd | |||
| e87145d6b9 | |||
| c405d5a5ed | |||
| 793c2ebf3f | |||
| 7247dca120 | |||
| 62d50b94b1 | |||
| 8091cea606 | |||
| 50b2b29133 | |||
| 87d31a739e | |||
| dc2c900470 | |||
| 2dbeda37ee | |||
| 108cc447b4 | |||
| 8a3f69c387 | |||
| 783a6535f7 | |||
| 9976af5090 | |||
| f43476441a | |||
| 165a4d6a49 | |||
| f3b2ac7136 | |||
| 37bc0017c6 | |||
| 5799cc5faa | |||
| e67f17aeb9 | |||
| 582c4edb53 | |||
| daa8cab29d | |||
| 99be85cd54 | |||
| 17177b3713 | |||
| a1fe7024ac | |||
| 67fbf5bbdb | |||
| 89fb4f5f08 | |||
| 98b27a1122 | |||
| 3112cb771b | |||
| 4312da6a00 | |||
| c562fef7b0 | |||
| df7bc370ff | |||
| 651ed73d41 | |||
| 3f3ef3327b | |||
| 8a93406af8 | |||
| 5e707dc67e | |||
| b1ffeb77b4 | |||
| 7cf226e941 | |||
| e418f23948 | |||
| 0d0985b8ca |
@@ -4,21 +4,8 @@ Thumbs.db
|
||||
*~
|
||||
*.swp
|
||||
*.swo
|
||||
.obsidian
|
||||
.obsidian/
|
||||
.claude
|
||||
|
||||
# Obsidian device-only UI state
|
||||
.obsidian/workspace.json
|
||||
.obsidian/workspaces.json
|
||||
.obsidian/workspace-mobile.json
|
||||
.obsidian/workspaces-mobile.json
|
||||
|
||||
# Obsidian generated indexes / caches
|
||||
.obsidian/copilot-index-*.json
|
||||
.obsidian/cache/
|
||||
.obsidian/.cache/
|
||||
|
||||
# Obsidian plugin device-only runtime files
|
||||
.obsidian/plugins/**/cache/
|
||||
.obsidian/plugins/**/.cache/
|
||||
.obsidian/plugins/**/node_modules/
|
||||
.obsidian/plugins/typewriter-mode/cursor-positions.json
|
||||
.obsidian/plugins/obsidian-git/obsidian_askpass.sh
|
||||
!".obsidian\\plugins\\obsidian-git"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"attachmentFolderPath": "99 System/Cache",
|
||||
"trashOption": "local",
|
||||
"showUnsupportedFiles": true,
|
||||
"promptDelete": false,
|
||||
"promptDelete": true,
|
||||
"pdfExportSettings": {
|
||||
"includeName": false,
|
||||
"pageSize": "Legal",
|
||||
@@ -18,9 +18,10 @@
|
||||
"rightToLeft": false,
|
||||
"defaultViewMode": "preview",
|
||||
"showLineNumber": true,
|
||||
"openBehavior": "file:AGENTS",
|
||||
"openBehavior": "",
|
||||
"userIgnoreFilters": [
|
||||
"99 System/"
|
||||
],
|
||||
"showInlineTitle": true
|
||||
"showInlineTitle": true,
|
||||
"uriCallbacks": true
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"cssTheme": "AnuPpuccin",
|
||||
"accentColor": "#552db4",
|
||||
"accentColor": "",
|
||||
"enabledCssSnippets": [
|
||||
"math-blocks"
|
||||
],
|
||||
@@ -8,9 +8,9 @@
|
||||
"interfaceFontFamily": "Fira Sans",
|
||||
"textFontFamily": "Fira Sans",
|
||||
"baseFontSizeAction": true,
|
||||
"baseFontSize": 16,
|
||||
"baseFontSize": 15,
|
||||
"monospaceFontFamily": "FiraCode Nerd Font",
|
||||
"nativeMenus": false,
|
||||
"showRibbon": false,
|
||||
"showRibbon": true,
|
||||
"showViewHeader": false
|
||||
}
|
||||
@@ -12,5 +12,5 @@
|
||||
"obsidian-excalidraw-plugin",
|
||||
"terminal",
|
||||
"typewriter-mode",
|
||||
"3d-graph-new"
|
||||
"typographer"
|
||||
]
|
||||
@@ -3,9 +3,9 @@
|
||||
"global-search": true,
|
||||
"switcher": true,
|
||||
"graph": true,
|
||||
"backlink": true,
|
||||
"backlink": false,
|
||||
"canvas": true,
|
||||
"outgoing-link": true,
|
||||
"outgoing-link": false,
|
||||
"tag-pane": true,
|
||||
"footnotes": true,
|
||||
"properties": true,
|
||||
@@ -19,7 +19,7 @@
|
||||
"bookmarks": true,
|
||||
"markdown-importer": true,
|
||||
"zk-prefixer": false,
|
||||
"random-note": true,
|
||||
"random-note": false,
|
||||
"outline": true,
|
||||
"word-count": true,
|
||||
"slides": false,
|
||||
|
||||
@@ -39,6 +39,6 @@
|
||||
"repelStrength": 6.34095634095634,
|
||||
"linkStrength": 0.504158004158004,
|
||||
"linkDistance": 250,
|
||||
"scale": 0.2882782914407446,
|
||||
"scale": 0.21176529377630143,
|
||||
"close": true
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
{
|
||||
"savedSettings": [],
|
||||
"temporaryLocalGraphSetting": {
|
||||
"filter": {
|
||||
"searchQuery": "",
|
||||
"showOrphans": true,
|
||||
"showAttachments": false,
|
||||
"depth": 1,
|
||||
"linkType": "both"
|
||||
},
|
||||
"groups": [],
|
||||
"display": {
|
||||
"nodeSize": 3,
|
||||
"linkThickness": 2,
|
||||
"linkDistance": 100,
|
||||
"nodeRepulsion": 2800,
|
||||
"distanceFromFocal": 300,
|
||||
"nodeHoverColor": "#ff0000",
|
||||
"nodeHoverNeighbourColor": "#00ff00",
|
||||
"linkHoverColor": "#0000ff",
|
||||
"showExtension": false,
|
||||
"showFullPath": false,
|
||||
"showCenterCoordinates": true,
|
||||
"showLinkArrow": true,
|
||||
"dontMoveWhenDrag": false,
|
||||
"dagOrientation": "null"
|
||||
}
|
||||
},
|
||||
"temporaryGlobalGraphSetting": {
|
||||
"filter": {
|
||||
"searchQuery": "",
|
||||
"showOrphans": true,
|
||||
"showAttachments": false
|
||||
},
|
||||
"groups": [],
|
||||
"display": {
|
||||
"nodeSize": 3,
|
||||
"linkThickness": 2.2,
|
||||
"linkDistance": 72,
|
||||
"nodeRepulsion": 2900,
|
||||
"distanceFromFocal": 170,
|
||||
"nodeHoverColor": "#ff0000",
|
||||
"nodeHoverNeighbourColor": "#00ff00",
|
||||
"linkHoverColor": "#0000ff",
|
||||
"showExtension": false,
|
||||
"showFullPath": false,
|
||||
"showCenterCoordinates": true,
|
||||
"showLinkArrow": true,
|
||||
"dontMoveWhenDrag": false,
|
||||
"dagOrientation": "null"
|
||||
}
|
||||
},
|
||||
"pluginSetting": {
|
||||
"maxNodeNumber": 1000,
|
||||
"searchEngine": "default",
|
||||
"rightClickToPan": false,
|
||||
"commandLeftClickNode": "openNodeInNewTab",
|
||||
"commandRightClickNode": "focusNode"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"id": "3d-graph-new",
|
||||
"name": "3D Graph New",
|
||||
"version": "1.1.11",
|
||||
"description": "A 3D Graph for Obsidian",
|
||||
"author": "Hananoshika Yomaru",
|
||||
"authorUrl": "https://github.com/HananoshikaYomaru",
|
||||
"fundingUrl": {
|
||||
"buymeacoffee": "https://www.buymeacoffee.com/yomaru",
|
||||
"Github Sponsor": "https://github.com/sponsors/HananoshikaYomaru"
|
||||
},
|
||||
"isDesktopOnly": true
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
.graph-3d-view .tree-item.is-collapsed > .tree-item-children {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.graph-3d-view {
|
||||
padding: 0 !important;
|
||||
position: relative;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.graph-3d-view .graph-controls.is-collapsed > .graph-control-section {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.graph-control-section .tree-item-inner {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.graph-3d-view .graph-control-section.is-collapsed .tree-item-inner::before {
|
||||
content: "▶";
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.graph-3d-view .graph-control-section:not(.is-collapsed) .tree-item-inner::before {
|
||||
content: "▼";
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.graph-3d-view .graph-controls:hover > .control-buttons {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.graph-3d-view .graph-controls > .control-buttons:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.graph-3d-view .graph-controls > .control-buttons {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.graph-3d-view .hidden {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.graph-3d-view .control-buttons {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.graph-3d-view .control-buttons > * {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.graph-3d-view .graph-settings-view > .clickable-icon {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
}
|
||||
|
||||
.graph-3d-view .node-label {
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.graph-3d-view .scene-nav-info {
|
||||
display: block;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.mod-search-input {
|
||||
width: 100%;
|
||||
padding: 0.5em;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 0.25em;
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.graph-3d-view .graph-controls:not(.is-close) {
|
||||
max-height: calc(90% - var(--size-4-4));
|
||||
}
|
||||
|
||||
.workspace-leaf-content:has(.search-result-container.is-loading) .graph-controls {
|
||||
/* border color purple */
|
||||
border-color: var(--color-purple);
|
||||
|
||||
/* create the animation */
|
||||
animation: color-oscillation 2s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes color-oscillation {
|
||||
0% {
|
||||
border: 1px solid var(--color-purple);
|
||||
}
|
||||
50% {
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
100% {
|
||||
border: 1px solid var(--color-purple);
|
||||
}
|
||||
}
|
||||
|
||||
.workspace-leaf-content:has(.search-result-container.is-loading) .search-input-container input {
|
||||
animation: outline-gradient-animation 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes outline-gradient-animation {
|
||||
0% {
|
||||
box-shadow: 0 2px 0 var(--color-purple);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 2px 0 var(--background-modifier-border);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 2px 0 var(--color-purple);
|
||||
}
|
||||
}
|
||||
|
||||
/* section: setting tab */
|
||||
.graph-3d-setting-tab {
|
||||
> h2 {
|
||||
color: var(--color-purple);
|
||||
font-size: var(--h2-size);
|
||||
}
|
||||
|
||||
> h2 + .setting-item {
|
||||
border-top: 0px;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"lastVersion": "3.2.7",
|
||||
"aestheticStyle": "glass",
|
||||
"positionStyle": "top",
|
||||
"lastVersion": "4.0.8",
|
||||
"aestheticStyle": "custom",
|
||||
"positionStyle": "fixed",
|
||||
"menuCommands": [
|
||||
{
|
||||
"id": "editing-toolbar:editor-undo",
|
||||
@@ -411,43 +411,6 @@
|
||||
"name": "Clear text formatting",
|
||||
"icon": "eraser"
|
||||
},
|
||||
{
|
||||
"id": "editing-toolbar:header2-text",
|
||||
"name": "Header 2",
|
||||
"icon": "header-2"
|
||||
},
|
||||
{
|
||||
"id": "editing-toolbar:header3-text",
|
||||
"name": "Header 3",
|
||||
"icon": "header-3"
|
||||
},
|
||||
{
|
||||
"id": "SubmenuCommands-header",
|
||||
"name": "submenu",
|
||||
"icon": "header-n",
|
||||
"SubmenuCommands": [
|
||||
{
|
||||
"id": "editing-toolbar:header1-text",
|
||||
"name": "Header 1",
|
||||
"icon": "header-1"
|
||||
},
|
||||
{
|
||||
"id": "editing-toolbar:header4-text",
|
||||
"name": "Header 4",
|
||||
"icon": "header-4"
|
||||
},
|
||||
{
|
||||
"id": "editing-toolbar:header5-text",
|
||||
"name": "Header 5",
|
||||
"icon": "header-5"
|
||||
},
|
||||
{
|
||||
"id": "editing-toolbar:header6-text",
|
||||
"name": "Header 6",
|
||||
"icon": "header-6"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "editing-toolbar:toggle-bold",
|
||||
"name": "Bold",
|
||||
@@ -1075,7 +1038,7 @@
|
||||
"shouldShowMenuOnSelect": false,
|
||||
"cMenuVisibility": true,
|
||||
"cMenuBottomValue": 4.25,
|
||||
"cMenuNumRows": 12,
|
||||
"cMenuNumRows": 1,
|
||||
"cMenuWidth": 610,
|
||||
"cMenuFontColor": "#2DC26B",
|
||||
"cMenuBackgroundColor": "#d3f8b6",
|
||||
@@ -1108,13 +1071,13 @@
|
||||
"toolbarBackgroundColor": "rgba(var(--background-secondary-rgb), 0.7)",
|
||||
"toolbarIconColor": "var(--text-normal)",
|
||||
"toolbarIconSize": 18,
|
||||
"aestheticStyle": "default"
|
||||
"aestheticStyle": "glass"
|
||||
},
|
||||
"fixed": {
|
||||
"toolbarBackgroundColor": "rgba(var(--background-secondary-rgb), 0.7)",
|
||||
"toolbarIconColor": "var(--text-normal)",
|
||||
"toolbarIconSize": 18,
|
||||
"aestheticStyle": "default"
|
||||
"toolbarBackgroundColor": "#F5F8FA",
|
||||
"toolbarIconColor": "#4A5568",
|
||||
"toolbarIconSize": 12,
|
||||
"aestheticStyle": "custom"
|
||||
},
|
||||
"mobile": {
|
||||
"toolbarBackgroundColor": "rgba(var(--background-secondary-rgb), 0.7)",
|
||||
@@ -1123,9 +1086,101 @@
|
||||
"aestheticStyle": "default"
|
||||
}
|
||||
},
|
||||
"toolbarBackgroundColor": "#2D3033",
|
||||
"toolbarIconColor": "#E2E8F0",
|
||||
"toolbarIconSize": 18,
|
||||
"toolbarBackgroundColor": "#F5F8FA",
|
||||
"toolbarIconColor": "#4A5568",
|
||||
"toolbarIconSize": 12,
|
||||
"useCurrentLineForRegex": false,
|
||||
"ai": {
|
||||
"enabled": false,
|
||||
"consentAccepted": true,
|
||||
"onboardingShown": true,
|
||||
"providerMode": "pkmer-first",
|
||||
"enableInlineCompletion": true,
|
||||
"inlineCompletionHintLearned": false,
|
||||
"completionTrigger": "manual",
|
||||
"completionDelay": 500,
|
||||
"enableRewrite": true,
|
||||
"showRewriteToolbarOnSelection": false,
|
||||
"rewriteMinSelectionLength": 1,
|
||||
"pkmerApiBaseUrl": "https://newapi.pkmer.cn",
|
||||
"pkmerModel": "04-fast",
|
||||
"pkmerModelRouting": {
|
||||
"mode": "smart",
|
||||
"completion": "04-fast",
|
||||
"rewrite": "04-fast",
|
||||
"reasoning": "03-agent",
|
||||
"artifact": "03-agent"
|
||||
},
|
||||
"pkmer": {
|
||||
"tokenExpiresAt": 1781624499398,
|
||||
"userInfo": {
|
||||
"sub": "fa484d57-e07b-48f3-9410-ff0ce39782d8",
|
||||
"name": "gcdmitry",
|
||||
"email": "gcdmitry@gmail.com",
|
||||
"ai_quota": {
|
||||
"quota": 49875000,
|
||||
"usedQuota": 125000,
|
||||
"remainingQuota": 49875000,
|
||||
"remainingTokens": 99,
|
||||
"requestCount": 1
|
||||
},
|
||||
"thino": false,
|
||||
"supporter": false
|
||||
}
|
||||
},
|
||||
"enableCustomModel": false,
|
||||
"customModel": {
|
||||
"apiFormat": "openai-compatible",
|
||||
"baseUrl": "",
|
||||
"apiKey": "",
|
||||
"model": "",
|
||||
"temperature": 0.2
|
||||
},
|
||||
"customPromptHistory": [],
|
||||
"customPromptTemplates": [
|
||||
{
|
||||
"id": "template-demo-variables",
|
||||
"name": "📝 Summarize Key Points",
|
||||
"prompt": "Analyze the structure of {{file:content}} and focus on:\n{{selection}}\n\nPlease provide:\n1. A concise overview\n2. The key points\n3. Suggestions for improvement",
|
||||
"icon": "lucide-sparkles"
|
||||
},
|
||||
{
|
||||
"id": "template-task-variables",
|
||||
"name": "Extract Tasks",
|
||||
"prompt": "Today's date is {{date}}. Extract all actionable tasks from {{file:content}}, with special attention to:\n{{selection}}\n\nPlease output strictly in Obsidian Tasks plugin format, one task per line:\n\n- [ ] Task description ⏫/🔼/🔽/⏬ priority\n- [ ] Task description 📅 YYYY-MM-DD due date\n- [ ] Task description ⏰ YYYY-MM-DD HH:mm reminder\n- [ ] Task description 🛫 YYYY-MM-DD start date\n- [ ] Task description 🔁 every day/week/month recurrence\n- [ ] Task description #tag #project\n\nExtraction rules:\n1. If the text implies urgency such as urgent, ASAP, immediately, or today, add ⏫.\n2. If it implies near-term timing such as tomorrow, this week, or soon, add 🔼.\n3. Convert explicit dates to 📅 YYYY-MM-DD.\n4. Convert explicit times to ⏰ YYYY-MM-DD HH:mm when possible.\n5. Add recurrence for repeated work such as 🔁 every week/month.\n6. Add practical tags for each task.\n\nExample output:\n- [ ] Finish project report ⏫ 📅 2026-04-25 #work\n- [ ] Weekly team sync 🔁 every week on Monday ⏰ 09:00 #meeting\n- [ ] Follow up on client request 🔼 📅 2026-04-23 #follow-up",
|
||||
"icon": "lucide-sparkles"
|
||||
},
|
||||
{
|
||||
"id": "template-dataview",
|
||||
"name": "Generate Dataview",
|
||||
"prompt": "Help me generate an Obsidian Dataview query block based on my requirement. Requirements:\n1. Use DataviewJS or DQL syntax.\n2. Include the necessary filters and sorting.\n3. Add short comments explaining each part.\n4. Use DataviewJS if the logic is complex.\n\nMy requirement:",
|
||||
"icon": "lucide-database"
|
||||
},
|
||||
{
|
||||
"id": "template-templater",
|
||||
"name": "Design Templater Template",
|
||||
"prompt": "Help me design an Obsidian Templater template. Requirements:\n1. Use Templater syntax (<% %>).\n2. Include dynamic date, time, and similar variables.\n3. Support user input prompts.\n4. Add necessary conditionals and loops.\n5. Comment the purpose of each section.\n\nTemplate purpose:",
|
||||
"icon": "lucide-file-code"
|
||||
},
|
||||
{
|
||||
"id": "template-mermaid",
|
||||
"name": "Create Mermaid Diagram",
|
||||
"prompt": "Based on my selected text {{selection}}, generate Mermaid diagram code. Requirements:\n1. Choose an appropriate diagram type such as flowchart, sequence, class, or gantt.\n2. Use clear node names.\n3. Add useful styling and comments when needed.\n4. Ensure the syntax is valid and renderable.\n",
|
||||
"icon": "lucide-workflow"
|
||||
},
|
||||
{
|
||||
"id": "template-metadata",
|
||||
"name": "Design YAML",
|
||||
"prompt": "Based on the current note content {{file:content}}, help me design a suitable YAML Frontmatter structure for this note. Requirements:\n1. Recommend fields that fit the note content.\n2. Include common fields such as tags, aliases, and date.\n3. Suggest useful custom fields.\n4. Briefly explain the purpose of each field.\n\nNote type:",
|
||||
"icon": "lucide-file-json"
|
||||
},
|
||||
{
|
||||
"id": "template-callout",
|
||||
"name": "Wrap with Callout",
|
||||
"prompt": "Based on my selected text {{selection}}, wrap it using an Obsidian Callout block. Requirements:\n1. Choose an appropriate callout type such as note, tip, warning, or danger.\n2. Support nesting and folding when helpful.\n3. Include a title and content.\n4. Allow code blocks or lists when needed.\n\nContent requirement:",
|
||||
"icon": "lucide-message-square"
|
||||
}
|
||||
]
|
||||
},
|
||||
"commandIdsFixed": true
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "editing-toolbar",
|
||||
"name": "Editing Toolbar",
|
||||
"version": "3.2.7",
|
||||
"version": "4.0.8",
|
||||
"minAppVersion": "0.14.0",
|
||||
"description": "The Obsidian Editing Toolbar is modified from cmenu, which provides more powerful customization settings and has many built-in editing commands to be a MS Word-like toolbar editing experience.",
|
||||
"author": "Cuman",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "metadata-menu",
|
||||
"name": "Metadata Menu",
|
||||
"version": "0.8.11",
|
||||
"version": "0.8.12",
|
||||
"minAppVersion": "1.4.16",
|
||||
"description": "For data quality enthusiasts (and dataview users): manage the metadata of your notes.",
|
||||
"author": "mdelobelle",
|
||||
|
||||
@@ -189,6 +189,12 @@
|
||||
stroke: var(--color-green);
|
||||
}
|
||||
|
||||
.metadata-menu.modal-container .modal {
|
||||
padding-top: 40px;
|
||||
}
|
||||
.metadata-menu.modal-container .modal .modal-close-button {
|
||||
padding-top: 35px;
|
||||
}
|
||||
.metadata-menu.modal-container.narrow .modal {
|
||||
width: initial;
|
||||
}
|
||||
@@ -252,7 +258,7 @@
|
||||
font-style: italic;
|
||||
color: var(--color-accent);
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.modal-container .field-container .info {
|
||||
display: none;
|
||||
}
|
||||
@@ -338,7 +344,7 @@
|
||||
.metadata-menu.modal-container .field-container .date-input-wrapper .master-input.datetime {
|
||||
width: 14em;
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.modal-container .field-container .date-input-wrapper .master-input {
|
||||
display: none;
|
||||
}
|
||||
@@ -348,7 +354,6 @@
|
||||
margin-right: 0;
|
||||
}
|
||||
.metadata-menu.modal-container .field-container .date-input-wrapper .time-picker {
|
||||
width: 2em;
|
||||
margin-right: 0.5em;
|
||||
font-size: large;
|
||||
}
|
||||
@@ -624,7 +629,7 @@
|
||||
width: auto;
|
||||
min-width: var(--dialog-width);
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.modal-container.note-fields-modal .modal {
|
||||
max-width: 97vw;
|
||||
min-width: 0px;
|
||||
@@ -638,7 +643,7 @@
|
||||
grid-template-columns: auto auto 1fr auto;
|
||||
margin-top: 3px;
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.modal-container.note-fields-modal .note-fields-container {
|
||||
grid-template-columns: 270px 75px;
|
||||
}
|
||||
@@ -650,7 +655,7 @@
|
||||
grid-column: 2;
|
||||
display: flex;
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.modal-container.note-fields-modal .note-fields-container .field-settings-wrapper .field-settings-spacer {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -658,7 +663,7 @@
|
||||
.metadata-menu.modal-container.note-fields-modal .note-fields-container .field-value-wrapper {
|
||||
grid-column: 3;
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.modal-container.note-fields-modal .note-fields-container .field-value-wrapper {
|
||||
grid-column: 1;
|
||||
margin-bottom: 1em;
|
||||
@@ -668,7 +673,7 @@
|
||||
grid-column: 4;
|
||||
display: flex;
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.modal-container.note-fields-modal .note-fields-container .field-options-wrapper {
|
||||
grid-column: 2;
|
||||
margin-bottom: 1em;
|
||||
@@ -691,7 +696,7 @@
|
||||
padding-left: 0.6em;
|
||||
border: none;
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.modal-container.note-fields-modal .note-fields-container .field-name-wrapper .field-item.field-name, .metadata-menu.modal-container.note-fields-modal .note-fields-container .field-value-wrapper .field-item.field-name, .metadata-menu.modal-container.note-fields-modal .note-fields-container .field-settings-wrapper .field-item.field-name, .metadata-menu.modal-container.note-fields-modal .note-fields-container .field-options-wrapper .field-item.field-name {
|
||||
padding-left: 0;
|
||||
}
|
||||
@@ -703,7 +708,7 @@
|
||||
.metadata-menu.modal-container.note-fields-modal .note-fields-container .field-name-wrapper .field-item .field-type, .metadata-menu.modal-container.note-fields-modal .note-fields-container .field-value-wrapper .field-item .field-type, .metadata-menu.modal-container.note-fields-modal .note-fields-container .field-settings-wrapper .field-item .field-type, .metadata-menu.modal-container.note-fields-modal .note-fields-container .field-options-wrapper .field-item .field-type {
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.modal-container.note-fields-modal .note-fields-container .field-name-wrapper .field-item .field-type.chip, .metadata-menu.modal-container.note-fields-modal .note-fields-container .field-value-wrapper .field-item .field-type.chip, .metadata-menu.modal-container.note-fields-modal .note-fields-container .field-settings-wrapper .field-item .field-type.chip, .metadata-menu.modal-container.note-fields-modal .note-fields-container .field-options-wrapper .field-item .field-type.chip {
|
||||
margin-right: 0;
|
||||
}
|
||||
@@ -758,6 +763,12 @@
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
|
||||
.metadata-menu.modal-container .modal {
|
||||
padding-top: 40px;
|
||||
}
|
||||
.metadata-menu.modal-container .modal .modal-close-button {
|
||||
padding-top: 35px;
|
||||
}
|
||||
.metadata-menu.modal-container.narrow .modal {
|
||||
width: initial;
|
||||
}
|
||||
@@ -821,7 +832,7 @@
|
||||
font-style: italic;
|
||||
color: var(--color-accent);
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.modal-container .field-container .info {
|
||||
display: none;
|
||||
}
|
||||
@@ -907,7 +918,7 @@
|
||||
.metadata-menu.modal-container .field-container .date-input-wrapper .master-input.datetime {
|
||||
width: 14em;
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.modal-container .field-container .date-input-wrapper .master-input {
|
||||
display: none;
|
||||
}
|
||||
@@ -917,7 +928,6 @@
|
||||
margin-right: 0;
|
||||
}
|
||||
.metadata-menu.modal-container .field-container .date-input-wrapper .time-picker {
|
||||
width: 2em;
|
||||
margin-right: 0.5em;
|
||||
font-size: large;
|
||||
}
|
||||
@@ -1361,7 +1371,7 @@
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-gap: 10px;
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.prompt.media-as-cards .prompt-results {
|
||||
grid-template-columns: auto;
|
||||
}
|
||||
@@ -1529,7 +1539,7 @@
|
||||
display: grid;
|
||||
grid-template-columns: auto auto auto 1fr;
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.fileclass-view .fv-fields .fields-container, .metadata-menu.fileclass-codeblock-view .fv-fields .fields-container {
|
||||
grid-template-columns: auto auto auto;
|
||||
}
|
||||
@@ -1541,7 +1551,7 @@
|
||||
font-weight: 500;
|
||||
display: inline-flex;
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.fileclass-view .fv-fields .fields-container .name-container, .metadata-menu.fileclass-codeblock-view .fv-fields .fields-container .name-container {
|
||||
margin-top: 1em;
|
||||
}
|
||||
@@ -1554,7 +1564,7 @@
|
||||
.metadata-menu.fileclass-view .fv-fields .fields-container .type-container, .metadata-menu.fileclass-codeblock-view .fv-fields .fields-container .type-container {
|
||||
grid-column: 2;
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.fileclass-view .fv-fields .fields-container .type-container, .metadata-menu.fileclass-codeblock-view .fv-fields .fields-container .type-container {
|
||||
margin-top: 1em;
|
||||
}
|
||||
@@ -1575,7 +1585,7 @@
|
||||
grid-column: 3;
|
||||
display: flex;
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.fileclass-view .fv-fields .fields-container .buttons-container, .metadata-menu.fileclass-codeblock-view .fv-fields .fields-container .buttons-container {
|
||||
margin-top: 1em;
|
||||
}
|
||||
@@ -1583,7 +1593,7 @@
|
||||
.metadata-menu.fileclass-view .fv-fields .fields-container .options-container, .metadata-menu.fileclass-codeblock-view .fv-fields .fields-container .options-container {
|
||||
grid-column: 4;
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.fileclass-view .fv-fields .fields-container .options-container, .metadata-menu.fileclass-codeblock-view .fv-fields .fields-container .options-container {
|
||||
grid-column: 1;
|
||||
grid-column-start: span 3;
|
||||
@@ -1594,7 +1604,7 @@
|
||||
margin-left: 1em;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.fileclass-view .fv-fields .fields-container .options-container .description, .metadata-menu.fileclass-codeblock-view .fv-fields .fields-container .options-container .description {
|
||||
margin-left: 0em;
|
||||
white-space: normal;
|
||||
@@ -1638,7 +1648,7 @@
|
||||
grid-template-columns: auto auto auto 1fr;
|
||||
align-items: center;
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.fileclass-view .fv-settings .settings-container, .metadata-menu.fileclass-codeblock-view .fv-settings .settings-container {
|
||||
grid-template-columns: 1fr auto;
|
||||
}
|
||||
@@ -1649,7 +1659,7 @@
|
||||
white-space: nowrap;
|
||||
font-weight: 500;
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.fileclass-view .fv-settings .settings-container .label, .metadata-menu.fileclass-codeblock-view .fv-settings .settings-container .label {
|
||||
margin-top: 1em;
|
||||
}
|
||||
@@ -1658,7 +1668,7 @@
|
||||
grid-column: 3;
|
||||
margin: 0.2em 0em 0.2em 0;
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.fileclass-view .fv-settings .settings-container .action, .metadata-menu.fileclass-codeblock-view .fv-settings .settings-container .action {
|
||||
grid-column: 1;
|
||||
grid-column-start: span 2;
|
||||
@@ -1719,7 +1729,7 @@
|
||||
.metadata-menu.fileclass-view .fv-settings .settings-container .tooltip-btn, .metadata-menu.fileclass-codeblock-view .fv-settings .settings-container .tooltip-btn {
|
||||
grid-column: 2;
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.fileclass-view .fv-settings .settings-container .tooltip-btn, .metadata-menu.fileclass-codeblock-view .fv-settings .settings-container .tooltip-btn {
|
||||
margin-top: 1em;
|
||||
}
|
||||
@@ -1740,7 +1750,7 @@
|
||||
line-break: strict;
|
||||
line-height: normal;
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.fileclass-view .fv-settings .settings-container .tooltip-text, .metadata-menu.fileclass-codeblock-view .fv-settings .settings-container .tooltip-text {
|
||||
grid-column: 1;
|
||||
grid-column-start: span 2;
|
||||
@@ -1781,7 +1791,7 @@
|
||||
}
|
||||
*/
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 440px) {
|
||||
.metadata-menu.fileclass-view .fv-table .options, .metadata-menu.fileclass-codeblock-view .fv-table .options {
|
||||
max-height: 50em;
|
||||
overflow: scroll;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"showTabTitlebarButtons": true,
|
||||
"copyLinkToElemenetAnchorTo100": false,
|
||||
"copyFrameLinkByName": false,
|
||||
"disableDoubleClickTextEditing": false,
|
||||
@@ -33,6 +34,7 @@
|
||||
"displaySVGInPreview": false,
|
||||
"previewImageType": "SVG",
|
||||
"renderingConcurrency": 3,
|
||||
"imageCacheRetentionDays": 30,
|
||||
"allowImageCache": true,
|
||||
"allowImageCacheInScene": true,
|
||||
"displayExportedImageIfAvailable": false,
|
||||
@@ -84,7 +86,7 @@
|
||||
"pageTransclusionCharLimit": 200,
|
||||
"wordWrappingDefault": 0,
|
||||
"removeTransclusionQuoteSigns": true,
|
||||
"iframelyAllowed": true,
|
||||
"oEmbedAllowed": false,
|
||||
"pngExportScale": 1,
|
||||
"exportWithTheme": true,
|
||||
"exportWithBackground": true,
|
||||
@@ -98,6 +100,7 @@
|
||||
"embedType": "excalidraw",
|
||||
"embedMarkdownCommentLinks": true,
|
||||
"embedWikiLink": true,
|
||||
"embedPlaceholderImage": true,
|
||||
"syncExcalidraw": false,
|
||||
"experimentalFileType": false,
|
||||
"experimentalFileTag": "✏️",
|
||||
@@ -109,13 +112,15 @@
|
||||
"addDummyTextElement": false,
|
||||
"zoteroCompatibility": false,
|
||||
"fieldSuggester": true,
|
||||
"enableOnloadScripts": false,
|
||||
"enableCommandLinks": false,
|
||||
"compatibilityMode": false,
|
||||
"drawingOpenCount": 0,
|
||||
"library": "deprecated",
|
||||
"library2": {
|
||||
"type": "excalidrawlib",
|
||||
"version": 2,
|
||||
"source": "https://github.com/zsviczian/obsidian-excalidraw-plugin/releases/tag/2.23.0",
|
||||
"source": "https://github.com/zsviczian/obsidian-excalidraw-plugin/releases/tag2.23.12",
|
||||
"libraryItems": []
|
||||
},
|
||||
"imageElementNotice": true,
|
||||
@@ -126,7 +131,7 @@
|
||||
"mdBorderColor": "Black",
|
||||
"mdCSS": "",
|
||||
"scriptEngineSettings": {},
|
||||
"previousRelease": "2.23.0",
|
||||
"previousRelease": "2.23.12",
|
||||
"showReleaseNotes": true,
|
||||
"excalidrawMasteryPromoCollapsed": false,
|
||||
"compareManifestToPluginVersion": false,
|
||||
@@ -489,15 +494,178 @@
|
||||
"canvasImmersiveEmbed": true,
|
||||
"startupScriptPath": "",
|
||||
"aiEnabled": true,
|
||||
"openAIAPIToken": "",
|
||||
"openAIDefaultTextModel": "gpt-5-mini",
|
||||
"openAIDefaultTextModelMaxTokens": 4096,
|
||||
"openAIDefaultVisionModel": "gpt-5-mini",
|
||||
"openAIDefaultImageGenerationModel": "gpt-image-1",
|
||||
"openAIURL": "https://api.openai.com/v1/chat/completions",
|
||||
"openAIImageGenerationURL": "https://api.openai.com/v1/images/generations",
|
||||
"openAIImageEditsURL": "https://api.openai.com/v1/images/edits",
|
||||
"openAIImageVariationURL": "https://api.openai.com/v1/images/variations",
|
||||
"aiVerboseLogging": false,
|
||||
"aiProviderProfiles": {
|
||||
"OpenAI": {
|
||||
"provider": "openai",
|
||||
"apiKey": "",
|
||||
"baseURL": "https://api.openai.com/v1"
|
||||
},
|
||||
"Anthropic": {
|
||||
"provider": "anthropic",
|
||||
"apiKey": "",
|
||||
"baseURL": "https://api.anthropic.com/v1"
|
||||
},
|
||||
"Google Gemini": {
|
||||
"provider": "google",
|
||||
"apiKey": "",
|
||||
"baseURL": "https://generativelanguage.googleapis.com/v1beta"
|
||||
},
|
||||
"xAI": {
|
||||
"provider": "xai",
|
||||
"apiKey": "",
|
||||
"baseURL": "https://api.x.ai/v1"
|
||||
},
|
||||
"OpenAI-compatible": {
|
||||
"provider": "openai-compatible",
|
||||
"apiKey": "",
|
||||
"baseURL": "https://api.openai.com/v1"
|
||||
}
|
||||
},
|
||||
"aiTextModelConfigs": {
|
||||
"gpt-5-mini": {
|
||||
"providerId": "OpenAI",
|
||||
"model": "gpt-5-mini",
|
||||
"endpoint": "",
|
||||
"multimodalSupport": true
|
||||
},
|
||||
"claude-sonnet-4-5": {
|
||||
"providerId": "Anthropic",
|
||||
"model": "claude-sonnet-4-5",
|
||||
"endpoint": "",
|
||||
"multimodalSupport": true
|
||||
},
|
||||
"gemini-2.5-pro": {
|
||||
"providerId": "Google Gemini",
|
||||
"model": "gemini-2.5-pro",
|
||||
"endpoint": "",
|
||||
"multimodalSupport": true
|
||||
},
|
||||
"grok-4-fast": {
|
||||
"providerId": "xAI",
|
||||
"model": "grok-4-fast",
|
||||
"endpoint": "",
|
||||
"multimodalSupport": true
|
||||
}
|
||||
},
|
||||
"aiImageModelConfigs": {
|
||||
"dall-e-2": {
|
||||
"providerId": "OpenAI",
|
||||
"model": "dall-e-2",
|
||||
"supportedSizes": [
|
||||
"256x256",
|
||||
"512x512",
|
||||
"1024x1024"
|
||||
],
|
||||
"supportsPromptImageTransforms": true,
|
||||
"supportsMaskImageEdits": true
|
||||
},
|
||||
"dall-e-3": {
|
||||
"providerId": "OpenAI",
|
||||
"model": "dall-e-3",
|
||||
"supportedSizes": [
|
||||
"1024x1024",
|
||||
"1792x1024",
|
||||
"1024x1792"
|
||||
],
|
||||
"supportsPromptImageTransforms": false,
|
||||
"supportsMaskImageEdits": false
|
||||
},
|
||||
"gpt-image-1": {
|
||||
"providerId": "OpenAI",
|
||||
"model": "gpt-image-1",
|
||||
"supportedSizes": [
|
||||
"1024x1024",
|
||||
"1536x1024",
|
||||
"1024x1536"
|
||||
],
|
||||
"supportsPromptImageTransforms": true,
|
||||
"supportsMaskImageEdits": true
|
||||
},
|
||||
"gpt-image-1-mini": {
|
||||
"providerId": "OpenAI",
|
||||
"model": "gpt-image-1-mini",
|
||||
"supportedSizes": [
|
||||
"1024x1024",
|
||||
"1536x1024",
|
||||
"1024x1536"
|
||||
],
|
||||
"supportsPromptImageTransforms": true,
|
||||
"supportsMaskImageEdits": true
|
||||
},
|
||||
"gpt-image-1.5": {
|
||||
"providerId": "OpenAI",
|
||||
"model": "gpt-image-1.5",
|
||||
"supportedSizes": [
|
||||
"1024x1024",
|
||||
"1536x1024",
|
||||
"1024x1536"
|
||||
],
|
||||
"supportsPromptImageTransforms": true,
|
||||
"supportsMaskImageEdits": true
|
||||
},
|
||||
"gpt-image-2": {
|
||||
"providerId": "OpenAI",
|
||||
"model": "gpt-image-2",
|
||||
"supportedSizes": [
|
||||
"1024x1024",
|
||||
"1536x1024",
|
||||
"1024x1536",
|
||||
"2048x2048"
|
||||
],
|
||||
"supportsPromptImageTransforms": true,
|
||||
"supportsMaskImageEdits": true
|
||||
},
|
||||
"gemini-2.5-flash-image": {
|
||||
"providerId": "Google Gemini",
|
||||
"model": "gemini-2.5-flash-image",
|
||||
"supportedSizes": [
|
||||
"1024x1024"
|
||||
],
|
||||
"supportsPromptImageTransforms": true,
|
||||
"supportsMaskImageEdits": false
|
||||
},
|
||||
"gemini-3.1-flash-image-preview": {
|
||||
"providerId": "Google Gemini",
|
||||
"model": "gemini-3.1-flash-image-preview",
|
||||
"supportedSizes": [
|
||||
"1024x1024"
|
||||
],
|
||||
"supportsPromptImageTransforms": true,
|
||||
"supportsMaskImageEdits": false
|
||||
},
|
||||
"gemini-3-pro-image-preview": {
|
||||
"providerId": "Google Gemini",
|
||||
"model": "gemini-3-pro-image-preview",
|
||||
"supportedSizes": [
|
||||
"1024x1024"
|
||||
],
|
||||
"supportsPromptImageTransforms": true,
|
||||
"supportsMaskImageEdits": false
|
||||
},
|
||||
"grok-imagine-image-quality": {
|
||||
"providerId": "xAI",
|
||||
"model": "grok-imagine-image-quality",
|
||||
"supportedSizes": [
|
||||
"1024x1024"
|
||||
],
|
||||
"supportsPromptImageTransforms": true,
|
||||
"supportsMaskImageEdits": false
|
||||
},
|
||||
"grok-imagine-image-pro": {
|
||||
"providerId": "xAI",
|
||||
"model": "grok-imagine-image-pro",
|
||||
"supportedSizes": [
|
||||
"1024x1024"
|
||||
],
|
||||
"supportsPromptImageTransforms": true,
|
||||
"supportsMaskImageEdits": false
|
||||
}
|
||||
},
|
||||
"aiDefaultTextModel": "gpt-5-mini",
|
||||
"aiDefaultImageGenerationModel": "gpt-image-1",
|
||||
"aiDefaultMaxOutgoingTokens": 0,
|
||||
"aiDefaultMaxResponseTokens": 4096,
|
||||
"modifierKeyConfig": {
|
||||
"Mac": {
|
||||
"LocalFileDragAction": {
|
||||
@@ -787,7 +955,6 @@
|
||||
"longPressDesktop": 500,
|
||||
"longPressMobile": 500,
|
||||
"doubleClickLinkOpenViewMode": true,
|
||||
"isDebugMode": false,
|
||||
"rank": "Bronze",
|
||||
"modifierKeyOverrides": [
|
||||
{
|
||||
@@ -820,178 +987,5 @@
|
||||
"margin": "normal"
|
||||
},
|
||||
"disableContextMenu": false,
|
||||
"imageCacheRetentionDays": 30,
|
||||
"enableOnloadScripts": false,
|
||||
"aiVerboseLogging": false,
|
||||
"aiProviderProfiles": {
|
||||
"OpenAI": {
|
||||
"provider": "openai",
|
||||
"apiKey": "",
|
||||
"baseURL": "https://api.openai.com/v1"
|
||||
},
|
||||
"Anthropic": {
|
||||
"provider": "anthropic",
|
||||
"apiKey": "",
|
||||
"baseURL": "https://api.anthropic.com/v1"
|
||||
},
|
||||
"Google Gemini": {
|
||||
"provider": "google",
|
||||
"apiKey": "",
|
||||
"baseURL": "https://generativelanguage.googleapis.com/v1beta"
|
||||
},
|
||||
"xAI": {
|
||||
"provider": "xai",
|
||||
"apiKey": "",
|
||||
"baseURL": "https://api.x.ai/v1"
|
||||
},
|
||||
"OpenAI-compatible": {
|
||||
"provider": "openai-compatible",
|
||||
"apiKey": "",
|
||||
"baseURL": "https://api.openai.com/v1"
|
||||
}
|
||||
},
|
||||
"aiTextModelConfigs": {
|
||||
"gpt-5-mini": {
|
||||
"providerId": "OpenAI",
|
||||
"model": "gpt-5-mini",
|
||||
"endpoint": "",
|
||||
"multimodalSupport": true
|
||||
},
|
||||
"claude-sonnet-4-5": {
|
||||
"providerId": "Anthropic",
|
||||
"model": "claude-sonnet-4-5",
|
||||
"endpoint": "",
|
||||
"multimodalSupport": true
|
||||
},
|
||||
"gemini-2.5-pro": {
|
||||
"providerId": "Google Gemini",
|
||||
"model": "gemini-2.5-pro",
|
||||
"endpoint": "",
|
||||
"multimodalSupport": true
|
||||
},
|
||||
"grok-4-fast": {
|
||||
"providerId": "xAI",
|
||||
"model": "grok-4-fast",
|
||||
"endpoint": "",
|
||||
"multimodalSupport": true
|
||||
}
|
||||
},
|
||||
"aiImageModelConfigs": {
|
||||
"dall-e-2": {
|
||||
"providerId": "OpenAI",
|
||||
"model": "dall-e-2",
|
||||
"supportedSizes": [
|
||||
"256x256",
|
||||
"512x512",
|
||||
"1024x1024"
|
||||
],
|
||||
"supportsPromptImageTransforms": true,
|
||||
"supportsMaskImageEdits": true
|
||||
},
|
||||
"dall-e-3": {
|
||||
"providerId": "OpenAI",
|
||||
"model": "dall-e-3",
|
||||
"supportedSizes": [
|
||||
"1024x1024",
|
||||
"1792x1024",
|
||||
"1024x1792"
|
||||
],
|
||||
"supportsPromptImageTransforms": false,
|
||||
"supportsMaskImageEdits": false
|
||||
},
|
||||
"gpt-image-1": {
|
||||
"providerId": "OpenAI",
|
||||
"model": "gpt-image-1",
|
||||
"supportedSizes": [
|
||||
"1024x1024",
|
||||
"1536x1024",
|
||||
"1024x1536"
|
||||
],
|
||||
"supportsPromptImageTransforms": true,
|
||||
"supportsMaskImageEdits": true
|
||||
},
|
||||
"gpt-image-1-mini": {
|
||||
"providerId": "OpenAI",
|
||||
"model": "gpt-image-1-mini",
|
||||
"supportedSizes": [
|
||||
"1024x1024",
|
||||
"1536x1024",
|
||||
"1024x1536"
|
||||
],
|
||||
"supportsPromptImageTransforms": true,
|
||||
"supportsMaskImageEdits": true
|
||||
},
|
||||
"gpt-image-1.5": {
|
||||
"providerId": "OpenAI",
|
||||
"model": "gpt-image-1.5",
|
||||
"supportedSizes": [
|
||||
"1024x1024",
|
||||
"1536x1024",
|
||||
"1024x1536"
|
||||
],
|
||||
"supportsPromptImageTransforms": true,
|
||||
"supportsMaskImageEdits": true
|
||||
},
|
||||
"gpt-image-2": {
|
||||
"providerId": "OpenAI",
|
||||
"model": "gpt-image-2",
|
||||
"supportedSizes": [
|
||||
"1024x1024",
|
||||
"1536x1024",
|
||||
"1024x1536",
|
||||
"2048x2048"
|
||||
],
|
||||
"supportsPromptImageTransforms": true,
|
||||
"supportsMaskImageEdits": true
|
||||
},
|
||||
"gemini-2.5-flash-image": {
|
||||
"providerId": "Google Gemini",
|
||||
"model": "gemini-2.5-flash-image",
|
||||
"supportedSizes": [
|
||||
"1024x1024"
|
||||
],
|
||||
"supportsPromptImageTransforms": true,
|
||||
"supportsMaskImageEdits": false
|
||||
},
|
||||
"gemini-3.1-flash-image-preview": {
|
||||
"providerId": "Google Gemini",
|
||||
"model": "gemini-3.1-flash-image-preview",
|
||||
"supportedSizes": [
|
||||
"1024x1024"
|
||||
],
|
||||
"supportsPromptImageTransforms": true,
|
||||
"supportsMaskImageEdits": false
|
||||
},
|
||||
"gemini-3-pro-image-preview": {
|
||||
"providerId": "Google Gemini",
|
||||
"model": "gemini-3-pro-image-preview",
|
||||
"supportedSizes": [
|
||||
"1024x1024"
|
||||
],
|
||||
"supportsPromptImageTransforms": true,
|
||||
"supportsMaskImageEdits": false
|
||||
},
|
||||
"grok-imagine-image-quality": {
|
||||
"providerId": "xAI",
|
||||
"model": "grok-imagine-image-quality",
|
||||
"supportedSizes": [
|
||||
"1024x1024"
|
||||
],
|
||||
"supportsPromptImageTransforms": true,
|
||||
"supportsMaskImageEdits": false
|
||||
},
|
||||
"grok-imagine-image-pro": {
|
||||
"providerId": "xAI",
|
||||
"model": "grok-imagine-image-pro",
|
||||
"supportedSizes": [
|
||||
"1024x1024"
|
||||
],
|
||||
"supportsPromptImageTransforms": true,
|
||||
"supportsMaskImageEdits": false
|
||||
}
|
||||
},
|
||||
"aiDefaultTextModel": "gpt-5-mini",
|
||||
"aiDefaultImageGenerationModel": "gpt-image-1",
|
||||
"aiDefaultMaxOutgoingTokens": 0,
|
||||
"aiDefaultMaxResponseTokens": 4096
|
||||
"isDebugMode": false
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "2.23.0",
|
||||
"minAppVersion": "1.5.7",
|
||||
"version": "2.23.12",
|
||||
"minAppVersion": "1.8.7",
|
||||
"description": "Sketch Your Mind. Edit and view Excalidraw drawings. Enter the world of 4D Visual PKM.",
|
||||
"author": "Zsolt Viczian",
|
||||
"authorUrl": "https://excalidraw-obsidian.online",
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
{
|
||||
"commitMessage": "vault backup: {{date}}",
|
||||
"autoCommitMessage": "vault backup: {{date}}",
|
||||
"commitMessage": "{{hostname}}: {{date}} | {{numFiles}}",
|
||||
"autoCommitMessage": "{{hostname}}: {{date}} | {{numFiles}}",
|
||||
"commitMessageScript": "",
|
||||
"commitDateFormat": "YYYY-MM-DD HH:mm:ss",
|
||||
"autoSaveInterval": 5,
|
||||
"autoPushInterval": 5,
|
||||
"autoPullInterval": 0,
|
||||
"autoPullOnBoot": false,
|
||||
"autoPullOnBoot": true,
|
||||
"autoCommitOnlyStaged": false,
|
||||
"disablePush": false,
|
||||
"pullBeforePush": false,
|
||||
"pullBeforePush": true,
|
||||
"disablePopups": false,
|
||||
"showErrorNotices": true,
|
||||
"disablePopupsForNoChanges": false,
|
||||
"listChangedFilesInMessageBody": false,
|
||||
"listChangedFilesInMessageBody": true,
|
||||
"showStatusBar": true,
|
||||
"updateSubmodules": false,
|
||||
"syncMethod": "reset",
|
||||
"syncMethod": "merge",
|
||||
"mergeStrategy": "none",
|
||||
"customMessageOnAutoBackup": false,
|
||||
"autoBackupAfterFileChange": false,
|
||||
@@ -28,11 +28,11 @@
|
||||
"showedMobileNotice": true,
|
||||
"refreshSourceControlTimer": 7000,
|
||||
"showBranchStatusBar": true,
|
||||
"setLastSaveToLastCommit": false,
|
||||
"setLastSaveToLastCommit": true,
|
||||
"submoduleRecurseCheckout": false,
|
||||
"gitDir": "",
|
||||
"showFileMenu": true,
|
||||
"authorInHistoryView": "hide",
|
||||
"authorInHistoryView": "initials",
|
||||
"dateInHistoryView": false,
|
||||
"diffStyle": "split",
|
||||
"hunks": {
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"description": "Integrate Git version control with automatic backup and other advanced features.",
|
||||
"isDesktopOnly": false,
|
||||
"fundingUrl": "https://ko-fi.com/vinzent",
|
||||
"version": "2.38.2"
|
||||
"version": "2.38.3"
|
||||
}
|
||||
|
||||
@@ -8,15 +8,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.git-signs-gutter {
|
||||
.cm-gutterElement {
|
||||
/* Needed to align the sign properly for different line heigts. Such as
|
||||
* when having a heading or list item.
|
||||
*/
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.workspace-leaf-content[data-type="git-view"] .button-border {
|
||||
border: 2px solid var(--interactive-accent);
|
||||
border-radius: var(--radius-s);
|
||||
@@ -108,7 +99,7 @@
|
||||
display: flex;
|
||||
}
|
||||
.git-tools .buttons > * {
|
||||
padding: 0 0;
|
||||
padding: 0;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@@ -175,7 +166,7 @@ which itself is adapted from the diff2html library with the following original l
|
||||
--git-change-bg: #ffd55840;
|
||||
--git-selected: #3572b0;
|
||||
|
||||
--git-delete: #c33;
|
||||
--git-delete: #cc3333;
|
||||
--git-insert: #399839;
|
||||
--git-change: #d0b44c;
|
||||
--git-move: #3572b0;
|
||||
@@ -538,13 +529,24 @@ which itself is adapted from the diff2html library with the following original l
|
||||
.d2h-diff-tbody {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* My additions */
|
||||
.cm-merge-revert {
|
||||
width: 4em;
|
||||
}
|
||||
/* Ensure that merge revert markers are positioned correctly */
|
||||
.cm-merge-revert > * {
|
||||
position: absolute;
|
||||
background-color: var(--background-secondary);
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
/* ====================== Line Authoring Information ====================== */
|
||||
|
||||
.cm-gutterElement.obs-git-blame-gutter {
|
||||
/* Add background color to spacing inbetween and around the gutter for better aesthetics */
|
||||
border-width: 0px 2px 0.2px 2px;
|
||||
border-width: 0px 2px 0.2px;
|
||||
border-style: solid;
|
||||
border-color: var(--background-secondary);
|
||||
background-color: var(--background-secondary);
|
||||
@@ -557,7 +559,7 @@ which itself is adapted from the diff2html library with the following original l
|
||||
font-family: monospace;
|
||||
height: 100%; /* ensure, that age-based background color occupies entire parent */
|
||||
text-align: right;
|
||||
padding: 0px 6px 0px 6px;
|
||||
padding: 0px 6px;
|
||||
white-space: pre; /* Keep spaces and do not collapse them. */
|
||||
}
|
||||
|
||||
@@ -602,6 +604,11 @@ which itself is adapted from the diff2html library with the following original l
|
||||
.git-signs-gutter {
|
||||
.cm-gutterElement {
|
||||
display: grid;
|
||||
|
||||
/* Needed to align the sign properly for different line heigts. Such as
|
||||
* when having a heading or list item.
|
||||
*/
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -669,18 +676,6 @@ div:hover > .git-gutter-marker.git-changedelete {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.git-diff {
|
||||
.cm-merge-revert {
|
||||
width: 4em;
|
||||
}
|
||||
/* Ensure that merge revert markers are positioned correctly */
|
||||
.cm-merge-revert > * {
|
||||
position: absolute;
|
||||
background-color: var(--background-secondary);
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
/* Prevent shifting of the editor when git signs gutter is the only gutter present */
|
||||
.cm-gutters.cm-gutters-before:has(> .git-signs-gutter:only-child) {
|
||||
margin-inline-end: 0;
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
"emojiStyle": "native",
|
||||
"iconColor": null,
|
||||
"recentlyUsedIcons": [
|
||||
"🧰",
|
||||
"🎒",
|
||||
"LiComputer",
|
||||
"💡",
|
||||
"🏔"
|
||||
"LiToolCase",
|
||||
"LiLibrary",
|
||||
"LiCalendar",
|
||||
"LiNotebook",
|
||||
"LiLightbulb"
|
||||
],
|
||||
"recentlyUsedIconsSize": 5,
|
||||
"rules": [],
|
||||
@@ -34,17 +34,16 @@
|
||||
"debugMode": false,
|
||||
"useInternalPlugins": false
|
||||
},
|
||||
"00 Inbox": "📥",
|
||||
"99 System": "🧰",
|
||||
"99 System": "LiToolCase",
|
||||
"99 System/Template": "LiBookTemplate",
|
||||
"99 System/Archive": "LiFolderArchive",
|
||||
"99 System/Cache": "LiArchive",
|
||||
"02 Projects": "📔",
|
||||
"03 Journal": "📅",
|
||||
"02 Projects": "LiNotebook",
|
||||
"03 Journal": "LiCalendar",
|
||||
"02 Projects/Наука": "LiShieldQuestion",
|
||||
"02 Projects/Учеба": "LiNotebookTabs",
|
||||
"01 Ideas": "💡",
|
||||
"90 Library": "♎",
|
||||
"01 Ideas": "LiLightbulb",
|
||||
"90 Library": "LiLibrary",
|
||||
"90 Library/02 DevOps": "LiSpline",
|
||||
"90 Library/04 Networking": "LiNetwork",
|
||||
"90 Library/05 Operating System": "LiComputer",
|
||||
@@ -68,5 +67,6 @@
|
||||
"01 Library/09 Sport": "LiDumbbell",
|
||||
"01 Library/10 Finance": "LiCircleDollarSign",
|
||||
"01 Library/07 HomeLab": "LiServer",
|
||||
"01 Library/01 Admin": "🏔"
|
||||
"01 Library/01 Admin": "🏔",
|
||||
"00 Inbox": "LiInbox"
|
||||
}
|
||||
@@ -149,7 +149,7 @@
|
||||
"enabled": true,
|
||||
"single-quote-enabled": true,
|
||||
"single-quote-style": "''",
|
||||
"double-quote-enabled": true,
|
||||
"double-quote-enabled": false,
|
||||
"double-quote-style": "\"\""
|
||||
},
|
||||
"remove-consecutive-list-markers": {
|
||||
@@ -278,10 +278,17 @@
|
||||
"lintOnFileChange": false,
|
||||
"displayLintOnFileChangeNotice": false,
|
||||
"settingsConvertedToConfigKeyValues": true,
|
||||
"additionalFileExtensions": [],
|
||||
"foldersToIgnore": [
|
||||
"99 System"
|
||||
],
|
||||
"filesToIgnore": [],
|
||||
"filesToIgnore": [
|
||||
{
|
||||
"label": "Excalidraw",
|
||||
"match": ".*\\.excalidraw(\\.md)?$",
|
||||
"flags": "i"
|
||||
}
|
||||
],
|
||||
"linterLocale": "system-default",
|
||||
"logLevel": "ERROR",
|
||||
"lintCommands": [],
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"id": "obsidian-linter",
|
||||
"name": "Linter",
|
||||
"version": "1.31.0",
|
||||
"minAppVersion": "1.9.0",
|
||||
"version": "1.31.2",
|
||||
"minAppVersion": "1.12.0",
|
||||
"description": "Formats and styles your notes. It can be used to format YAML tags, aliases, arrays, and metadata; footnotes; headings; spacing; math blocks; regular markdown contents like list, italics, and bold styles; and more with the use of custom rule options as well.",
|
||||
"author": "Victor Tao",
|
||||
"authorUrl": "https://github.com/platers",
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
"shiba-theme-settings@@translucent-pane-style-settings": "shib-setting-default-frosted-glass",
|
||||
"shiba-theme-settings@@shib-transparent-setting-panel": false,
|
||||
"Plugin@@colorful-checkbox": true,
|
||||
"anuppuccin-theme-settings@@anuppuccin-theme-dark": "ctp-macchiato",
|
||||
"anuppuccin-theme-settings@@anuppuccin-theme-accents": "ctp-accent-sapphire",
|
||||
"anuppuccin-theme-settings@@anuppuccin-theme-dark": "ctp-mocha",
|
||||
"anuppuccin-theme-settings@@anuppuccin-theme-accents": "ctp-accent-lavender",
|
||||
"anuppuccin-theme-settings@@anp-active-line": "anp-current-line-border-only",
|
||||
"anuppuccin-theme-settings@@anp-pdf-blend-toggle-dark": false,
|
||||
"anuppuccin-theme-settings@@anp-alt-tab-style": "anp-alternate-tab-toggle",
|
||||
@@ -64,9 +64,7 @@
|
||||
"pln@@pln-attachments-pdf": true,
|
||||
"pln@@pln-attachments-images": true,
|
||||
"pln@@pln-hdcl": true,
|
||||
"anuppuccin-theme-settings@@anp-toggle-preview": false,
|
||||
"anuppuccin-theme-settings@@file-margins": 60,
|
||||
"anuppuccin-theme-settings@@anp-preview-width-pct": 75,
|
||||
"anuppuccin-theme-settings@@anp-toggle-preview": true,
|
||||
"anuppuccin-theme-settings@@anp-layout-select": "anp-card-layout",
|
||||
"anuppuccin-theme-settings@@anp-toggle-metadata": true,
|
||||
"anuppuccin-theme-settings@@anp-toggle-scrollbars": true,
|
||||
@@ -109,5 +107,6 @@
|
||||
"anuppuccin-theme-settings@@anp-translucency-opacity": 0.5,
|
||||
"anuppuccin-theme-settings@@anp-editor-font-source": "Inter",
|
||||
"anuppuccin-theme-settings@@anp-editor-font-lp": "Inter",
|
||||
"anuppuccin-theme-settings@@anp-editor-font-rv": "Inter"
|
||||
"anuppuccin-theme-settings@@anp-editor-font-rv": "Inter",
|
||||
"anuppuccin-theme-settings@@file-line-width": 900
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"ignoreDiacritics": true,
|
||||
"ignoreArabicDiacritics": false,
|
||||
"indexedFileTypes": [],
|
||||
"indexFilesWithoutExtension": false,
|
||||
"displayTitle": "",
|
||||
"PDFIndexing": false,
|
||||
"officeIndexing": false,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"id": "omnisearch",
|
||||
"name": "Omnisearch",
|
||||
"version": "1.28.0",
|
||||
"version": "1.29.3",
|
||||
"minAppVersion": "1.7.2",
|
||||
"description": "A search engine that just works",
|
||||
"description": "A search engine that just works.",
|
||||
"author": "Simon Cambier",
|
||||
"authorUrl": "https://github.com/scambier/obsidian-omnisearch",
|
||||
"authorUrl": "https://scambier.xyz",
|
||||
"fundingUrl": {
|
||||
"Github": "https://github.com/sponsors/scambier",
|
||||
"Ko-fi": "https://ko-fi.com/scambier"
|
||||
|
||||
@@ -63,7 +63,6 @@
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
|
||||
.omnisearch-result__image-container {
|
||||
flex-basis: 20%;
|
||||
text-align: end;
|
||||
@@ -133,3 +132,19 @@
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.omnisearch-result-highlight .cm-content ::selection,
|
||||
.omnisearch-result-highlight .cm-line::selection {
|
||||
background-color: var(
|
||||
--omnisearch-highlight-color,
|
||||
rgba(222, 183, 110, 1)
|
||||
) !important;
|
||||
color: var(--omnisearch-highlight-text-color, black) !important;
|
||||
}
|
||||
|
||||
.omnisearch-result-highlight .cm-selectionLayer .cm-selectionBackground {
|
||||
background-color: var(
|
||||
--omnisearch-highlight-color,
|
||||
rgba(222, 183, 110, 1)
|
||||
) !important;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
{
|
||||
"id": "templater-obsidian",
|
||||
"name": "Templater",
|
||||
"version": "2.17.0",
|
||||
"description": "Create and use templates",
|
||||
"minAppVersion": "1.5.0",
|
||||
"version": "2.20.5",
|
||||
"description": "Advanced templating and automation using handlebars-like syntax.",
|
||||
"minAppVersion": "1.12.2",
|
||||
"author": "SilentVoid",
|
||||
"authorUrl": "https://github.com/SilentVoid13",
|
||||
"fundingUrl": {
|
||||
"GitHub Sponser (Zachatoo, maintainer)": "https://github.com/sponsors/Zachatoo",
|
||||
"Ko-fi (Zachatoo, maintainer)": "https://ko-fi.com/zachatoo",
|
||||
"GitHub Sponser (SilentVoid13, creator)": "https://github.com/sponsors/SilentVoid13",
|
||||
"Paypal (SilentVoid13, creator)": "https://www.paypal.com/donate?hosted_button_id=U2SRGAFYXT32Q"
|
||||
},
|
||||
"helpUrl": "https://silentvoid13.github.io/Templater/",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
|
||||
@@ -221,6 +221,6 @@ textarea.templater-prompt-input:focus {
|
||||
}
|
||||
|
||||
li.CodeMirror-hint-active {
|
||||
background: #08f;
|
||||
background: #0088ff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,130 @@
|
||||
"exposeInternalModules": true,
|
||||
"focusOnNewInstance": true,
|
||||
"hideStatusBar": "focused",
|
||||
"interceptKeysWhenFocused": true,
|
||||
"interceptLogging": true,
|
||||
"keymappings": [
|
||||
{
|
||||
"action": "sendEscapeSequence",
|
||||
"actionArg": "\r",
|
||||
"alt": false,
|
||||
"ctrl": false,
|
||||
"key": "Enter",
|
||||
"meta": false,
|
||||
"platform": null,
|
||||
"shift": true
|
||||
},
|
||||
{
|
||||
"action": "scrollPages",
|
||||
"actionArg": -1,
|
||||
"alt": false,
|
||||
"ctrl": false,
|
||||
"key": "PageUp",
|
||||
"meta": false,
|
||||
"platform": null,
|
||||
"shift": false
|
||||
},
|
||||
{
|
||||
"action": "scrollPages",
|
||||
"actionArg": 1,
|
||||
"alt": false,
|
||||
"ctrl": false,
|
||||
"key": "PageDown",
|
||||
"meta": false,
|
||||
"platform": null,
|
||||
"shift": false
|
||||
},
|
||||
{
|
||||
"action": "scrollToTop",
|
||||
"actionArg": null,
|
||||
"alt": false,
|
||||
"ctrl": false,
|
||||
"key": "Home",
|
||||
"meta": false,
|
||||
"platform": null,
|
||||
"shift": false
|
||||
},
|
||||
{
|
||||
"action": "scrollToBottom",
|
||||
"actionArg": null,
|
||||
"alt": false,
|
||||
"ctrl": false,
|
||||
"key": "End",
|
||||
"meta": false,
|
||||
"platform": null,
|
||||
"shift": false
|
||||
},
|
||||
{
|
||||
"action": "sendEscapeSequence",
|
||||
"actionArg": "b",
|
||||
"alt": true,
|
||||
"ctrl": false,
|
||||
"key": "ArrowLeft",
|
||||
"meta": false,
|
||||
"platform": "darwin",
|
||||
"shift": false
|
||||
},
|
||||
{
|
||||
"action": "sendEscapeSequence",
|
||||
"actionArg": "f",
|
||||
"alt": true,
|
||||
"ctrl": false,
|
||||
"key": "ArrowRight",
|
||||
"meta": false,
|
||||
"platform": "darwin",
|
||||
"shift": false
|
||||
},
|
||||
{
|
||||
"action": "sendHexCode",
|
||||
"actionArg": "01",
|
||||
"alt": false,
|
||||
"ctrl": false,
|
||||
"key": "ArrowLeft",
|
||||
"meta": true,
|
||||
"platform": "darwin",
|
||||
"shift": false
|
||||
},
|
||||
{
|
||||
"action": "sendHexCode",
|
||||
"actionArg": "05",
|
||||
"alt": false,
|
||||
"ctrl": false,
|
||||
"key": "ArrowRight",
|
||||
"meta": true,
|
||||
"platform": "darwin",
|
||||
"shift": false
|
||||
},
|
||||
{
|
||||
"action": "sendHexCode",
|
||||
"actionArg": "17",
|
||||
"alt": true,
|
||||
"ctrl": false,
|
||||
"key": "Backspace",
|
||||
"meta": false,
|
||||
"platform": "darwin",
|
||||
"shift": false
|
||||
},
|
||||
{
|
||||
"action": "sendHexCode",
|
||||
"actionArg": "15",
|
||||
"alt": false,
|
||||
"ctrl": false,
|
||||
"key": "Backspace",
|
||||
"meta": true,
|
||||
"platform": "darwin",
|
||||
"shift": false
|
||||
},
|
||||
{
|
||||
"action": "sendEscapeSequence",
|
||||
"actionArg": "d",
|
||||
"alt": true,
|
||||
"ctrl": false,
|
||||
"key": "Delete",
|
||||
"meta": false,
|
||||
"platform": "darwin",
|
||||
"shift": false
|
||||
}
|
||||
],
|
||||
"language": "",
|
||||
"macOSOptionKeyPassthrough": true,
|
||||
"newInstanceBehavior": "newHorizontalSplit",
|
||||
@@ -32,6 +155,7 @@
|
||||
},
|
||||
"linuxExternalDefault": {
|
||||
"args": [],
|
||||
"environment": [],
|
||||
"executable": "xterm",
|
||||
"followTheme": true,
|
||||
"name": "",
|
||||
@@ -52,6 +176,7 @@
|
||||
},
|
||||
"linuxIntegratedDefault": {
|
||||
"args": [],
|
||||
"environment": [],
|
||||
"executable": "/bin/sh",
|
||||
"followTheme": true,
|
||||
"name": "",
|
||||
@@ -76,6 +201,7 @@
|
||||
"args": [
|
||||
"--login"
|
||||
],
|
||||
"environment": [],
|
||||
"executable": "/usr/bin/zsh",
|
||||
"followTheme": true,
|
||||
"name": "ZSH",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"Buy Me a Coffee": "https://buymeacoffee.com/polyipseity",
|
||||
"GitHub Sponsors": "https://github.com/sponsors/polyipseity"
|
||||
},
|
||||
"version": "3.24.0",
|
||||
"version": "3.26.0",
|
||||
"authorUrl": "https://github.com/polyipseity",
|
||||
"id": "terminal",
|
||||
"isDesktopOnly": false,
|
||||
|
||||
@@ -1,36 +1,69 @@
|
||||
{
|
||||
"version": null,
|
||||
"isAnnounceUpdatesEnabled": true,
|
||||
"isPluginActivated": true,
|
||||
"isTypewriterScrollEnabled": true,
|
||||
"isOnlyActivateAfterFirstInteractionEnabled": true,
|
||||
"isOnlyMaintainTypewriterOffsetWhenReachedEnabled": true,
|
||||
"isTypewriterOnlyUseCommandsEnabled": false,
|
||||
"typewriterOffset": 0.5,
|
||||
"isKeepLinesAboveAndBelowEnabled": false,
|
||||
"linesAboveAndBelow": 5,
|
||||
"isMaxCharsPerLineEnabled": false,
|
||||
"maxCharsPerLine": 64,
|
||||
"isDimUnfocusedEnabled": false,
|
||||
"isDimHighlightListParentEnabled": false,
|
||||
"isDimTableAsOneEnabled": true,
|
||||
"dimUnfocusedMode": "paragraphs",
|
||||
"dimUnfocusedEditorsBehavior": "dim",
|
||||
"dimmedOpacity": 0.25,
|
||||
"isPauseDimUnfocusedWhileScrollingEnabled": true,
|
||||
"isPauseDimUnfocusedWhileSelectingEnabled": true,
|
||||
"isHighlightCurrentLineEnabled": false,
|
||||
"isFadeLinesEnabled": false,
|
||||
"fadeLinesIntensity": 0.5,
|
||||
"isHighlightCurrentLineOnlyInFocusedEditorEnabled": false,
|
||||
"currentLineHighlightStyle": "box",
|
||||
"currentLineHighlightUnderlineThickness": 1,
|
||||
"currentLineHighlightColor-dark": "#444",
|
||||
"currentLineHighlightColor-light": "#ddd",
|
||||
"doesWritingFocusShowHeader": false,
|
||||
"doesWritingFocusShowStatusBar": false,
|
||||
"doesWritingFocusShowVignette": true,
|
||||
"isWritingFocusFullscreen": true,
|
||||
"writingFocusVignetteStyle": "box",
|
||||
"isRestoreCursorPositionEnabled": false
|
||||
"general": {
|
||||
"version": "1.4.0",
|
||||
"isAnnounceUpdatesEnabled": true,
|
||||
"isPluginActivated": true,
|
||||
"isOnlyActivateAfterFirstInteractionEnabled": true,
|
||||
"enabledPlatforms": "both",
|
||||
"enabledFilePaths": [],
|
||||
"disabledFilePaths": []
|
||||
},
|
||||
"typewriter": {
|
||||
"isTypewriterScrollEnabled": true,
|
||||
"isOnlyMaintainTypewriterOffsetWhenReachedEnabled": true,
|
||||
"isTypewriterOnlyUseCommandsEnabled": false,
|
||||
"typewriterOffset": 0.5
|
||||
},
|
||||
"keepLinesAboveAndBelow": {
|
||||
"isKeepLinesAboveAndBelowEnabled": false,
|
||||
"linesAboveAndBelow": 5
|
||||
},
|
||||
"maxChars": {
|
||||
"isMaxCharsPerLineEnabled": false,
|
||||
"maxCharsPerLine": 64
|
||||
},
|
||||
"dimming": {
|
||||
"isDimUnfocusedEnabled": false,
|
||||
"isDimUnfocusedOnlyInWritingFocusModeEnabled": false,
|
||||
"isDimHighlightListParentEnabled": false,
|
||||
"isDimTableAsOneEnabled": true,
|
||||
"dimUnfocusedMode": "paragraphs",
|
||||
"dimUnfocusedEditorsBehavior": "dim",
|
||||
"dimmedOpacity": 0.25,
|
||||
"isPauseDimUnfocusedWhileScrollingEnabled": true,
|
||||
"isPauseDimUnfocusedWhileSelectingEnabled": true
|
||||
},
|
||||
"currentLine": {
|
||||
"isHighlightCurrentLineEnabled": false,
|
||||
"isHighlightCurrentLineOnlyInWritingFocusModeEnabled": false,
|
||||
"isFadeLinesEnabled": false,
|
||||
"isFadeLinesOnlyInWritingFocusModeEnabled": false,
|
||||
"fadeLinesIntensity": 0.5,
|
||||
"isHighlightCurrentLineOnlyInFocusedEditorEnabled": false,
|
||||
"isPauseCurrentLineHighlightWhileScrollingEnabled": false,
|
||||
"isPauseCurrentLineHighlightWhileSelectingEnabled": false,
|
||||
"currentLineHighlightStyle": "box",
|
||||
"currentLineHighlightUnderlineThickness": 1,
|
||||
"currentLineHighlightColor-dark": "#444",
|
||||
"currentLineHighlightColor-light": "#ddd"
|
||||
},
|
||||
"writingFocus": {
|
||||
"doesWritingFocusShowHeader": false,
|
||||
"doesWritingFocusShowVignette": true,
|
||||
"doesWritingFocusShowStatusBar": false,
|
||||
"isWritingFocusFullscreen": true,
|
||||
"writingFocusVignetteStyle": "box",
|
||||
"writingFocusFontSize": 0
|
||||
},
|
||||
"restoreCursorPosition": {
|
||||
"isRestoreCursorPositionEnabled": false,
|
||||
"cursorPositions": {}
|
||||
},
|
||||
"hemingwayMode": {
|
||||
"isHemingwayModeEnabled": false,
|
||||
"isHemingwayModeOnlyInWritingFocusModeEnabled": false,
|
||||
"isAllowBackspaceInHemingwayModeEnabled": false,
|
||||
"isShowHemingwayModeStatusBarEnabled": true,
|
||||
"hemingwayModeStatusBarText": "Hemingway"
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@
|
||||
"authorUrl": "https://davis-riedel.de/en/software",
|
||||
"description": "Typewriter scroll, highlight current line, dim unfocused paragraphs and sentences, writing focus, restore cursor position and more.",
|
||||
"isDesktopOnly": false,
|
||||
"version": "1.1.0",
|
||||
"minAppVersion": "0.15.0",
|
||||
"version": "1.4.0",
|
||||
"minAppVersion": "1.11.0",
|
||||
"fundingUrl": {
|
||||
"Buy Me a Coffee": "https://www.buymeacoffee.com/davis.riedel",
|
||||
"GitHub Sponsors": "https://github.com/sponsors/davisriedel"
|
||||
|
||||
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 83 KiB |
@@ -0,0 +1,8 @@
|
||||
---
|
||||
status: seed
|
||||
type: concept
|
||||
tags: []
|
||||
created: 2025-12-17
|
||||
updated: 2026-06-09
|
||||
aliases: []
|
||||
---
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
status: seed
|
||||
type: concept
|
||||
tags: []
|
||||
created: 2025-12-17
|
||||
updated: 2026-06-09
|
||||
aliases: []
|
||||
---
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
status: seed
|
||||
type: concept
|
||||
tags: []
|
||||
created: 2025-12-17
|
||||
updated: 2026-02-27
|
||||
aliases: []
|
||||
---
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
status: seed
|
||||
type: concept
|
||||
tags: []
|
||||
created: 2025-12-17
|
||||
updated: 2026-06-07
|
||||
aliases: []
|
||||
---
|
||||
90 Library/Containers/Apache Mesos
|
||||
|
||||
C:\Users\ada\Documents\Vaults\SecondBrain\90 Library\Containers\Apache Mesos.md
|
||||
@@ -0,0 +1,223 @@
|
||||
---
|
||||
status: seed
|
||||
type: concept
|
||||
tags: []
|
||||
created: 2025-12-17
|
||||
updated: 2026-06-10
|
||||
aliases: []
|
||||
---
|
||||
|
||||
# Система контроля версий. Знакомство с Git
|
||||
|
||||
## Git
|
||||
|
||||
Неоспоримыми преимуществами Git являются:
|
||||
|
||||
- Высокая производительность.
|
||||
- Безопасность.
|
||||
- Гибкость в распределенных системах.
|
||||
- Прекрасные возможности для командной работы.
|
||||
|
||||
### Производительность
|
||||
|
||||
Высокая производительность Git обусловлена подходом по оптимизации внутренних процедур и использованию анализа содержимого файлов Git работает с файлами, храня объекты с содержимым каталога и метаданными их версий.
|
||||
|
||||
### Безопасность
|
||||
|
||||
Безопасность при работе с Git обеспечивается целостностью исходного кода и применением алгоритма шифрования SHA1.
|
||||
|
||||
Использование Git гарантирует подлинность истории изменений и защищает исходный код от тайного внесения изменений.
|
||||
|
||||
### Гибкость
|
||||
|
||||
Гибкость при работе с Git достигается за счет поддержки линейных и нелинейных циклов разработки, совместимости со множеством других информационных систем и популярных протоколов.
|
||||
|
||||
### Возможности для командной работы
|
||||
|
||||
Git обладает впечатляющими возможностями для командной работы за счет поддержки множества разнообразных моделей управления исходным кодом, удовлетворяющих нужды больших и маленьких команд, простых и сложных распределенных проектов
|
||||
|
||||
## Часть 3. Модели управления исходным кодом
|
||||
|
||||
Работа с версиями исходного кода в Git построена на основе использования:
|
||||
|
||||
- Коммитов (Commits).
|
||||
- Веток (Branches).
|
||||
- Слияния веток (Merge).
|
||||
- Сравнения версий (Diff).
|
||||
|
||||
Большой популярностью до сих пор пользуется модель под названием Gitflow.
|
||||
|
||||
### Gitflow
|
||||
|
||||
**Gitflow** — модель ветвления в Git, в которой используются основные ветки (Main, Кelease, Вevelop) и функциональные ветки (Feature).
|
||||
|
||||
В качестве основных веток в Gitflow часто используются:
|
||||
|
||||
- Main (Ex. master).
|
||||
- Release.
|
||||
- Develop (Dev).
|
||||
|
||||
В ветке Main хранится официальная история релизов, в Release ветках концентрируется функционал готово к выпуску релиза продукта, а ветка Develop предназначена для разработки функционала.
|
||||
|
||||
В качестве функциональных веток в `Gitflow` используются *Feature* ветки, ответвленные от основной ветки *Develop*.
|
||||
|
||||
Работа над каждым новым функционалом ведется командой в собственной *Feature-ветке*.
|
||||
После завершения разработки функционала, каждая *Feature-ветка* **сливается** с *Develop-веткой*.
|
||||
|
||||
Когда в *Develop-ветке* оказывается достаточно функционала для выпуска нового релиза, на основе Develop-ветки создается ветка Release.
|
||||
|
||||
После прохождения всех тестов и проверок, Release-ветка сливается с остальными основными ветками — Main и Develop.
|
||||
|
||||
**Весь цикл разработки в модели Gitflow можно визуализировать** **следующим образом:**
|
||||
|
||||

|
||||
|
||||
**Модель Gitflow обладает и недостатками:**
|
||||
|
||||
- Не лучшая совместимость с современными процессами CI/CD.
|
||||
- Не подходит для рабочих процессов, основывающихся на подходах, отличных от регулярного выпуска релизов.
|
||||
- Потенциально запутанная схема веток и трудности в восстановлении историчности их слияний в сложных проектах.
|
||||
|
||||
На данный момент, Gitflow является недостаточно универсальной моделью рабочего процесса разработки и считается устаревшей.
|
||||
|
||||
Предпочтительной для современных процессов разработки является модель магистральных рабочих процессов (TBD).
|
||||
|
||||
### TBD
|
||||
|
||||
**TBD (Trunk Based Development)** — альтернативная модель управления исходным кодом в Git на основе ветвления, пришедшая на смену устаревшей модели Gitflow.
|
||||
|
||||
Модель TBD основана на принципе использования одной главной ветки, называемой “магистралью” (Trunk).
|
||||
|
||||
Вся работа над новым функционалом ведется разработчиками именно в магистральной ветке, что исключает трудности, связанные со слиянием и неработающими сборками.
|
||||
|
||||
Команда разработки, ведущая работу над новым функционалом, сохраняет свои изменения только в Trunk-ветку и обеспечивает непрерывную сборку, тестирование и доставку нового функционала, не привязываясь к срокам и частоте выпуска релизов.
|
||||
|
||||
При использовании модели TBD и регулярном добавлении в основную Trunk-ветку функционала, а также настроенных стабильных процессов сборки и доставки продукта (CI/CD), выпустить релиз можно практически в любой момент.
|
||||
|
||||
**Модель TBD можно визуализировать следующим образом:**
|
||||
|
||||

|
||||
|
||||
Модель TDB быстро завоевала популярность в разветвленных проектах и прекрасно зарекомендовала себя в распределенных больших командах разработки за счет своей понятности, динамичности и совместимости с CI/CD-процессами.
|
||||
|
||||
## Часть 4. Знакомство с Gitlab
|
||||
|
||||
### Gitlab
|
||||
|
||||
**Gitlab** — это веб-приложение, обеспечивающее управление репозиториями программного кода в системе контроля версий Git.
|
||||
|
||||
Установить и использовать Gitlab можно на собственном сервере или в облачной инфраструктуре.
|
||||
|
||||
Gitlab позволяет:
|
||||
|
||||
- Создавать, изменять и удалять репозитории.
|
||||
- Управлять пользователями и их правами.
|
||||
- Автоматизировать процессы CI/CD и тестирования.
|
||||
- Полнофункционально взаимодействовать с исходным кодом из веб-интерфейса.
|
||||
|
||||
Основные преимущества Gitlab:
|
||||
|
||||
- Гибкие возможности по планированию командной разработки.
|
||||
- Создание и управление проектами.
|
||||
- Построение процессов тестирования кода.
|
||||
- Непрерывная сборка и доставка (CI/CD).
|
||||
- Наблюдаемость (Observability).
|
||||
|
||||
Gitlab позволяет эффективно поддерживать и развивать выбранный вами подход по управлению исходным кодом и командной разработкой (Gitflow, TDB и другие).
|
||||
|
||||
Работа с репозиториями в Gitlab осуществляется посредством создания, изменения, управления, удаления проектов и работы с пользователями.
|
||||
|
||||
Функциональность Gitlab позволяет задействовать инструменты по управлению ветками и коммитами пользователей, обеспечивая слаженную командную работу.
|
||||
|
||||
В Gitlab реализованы инструменты для проведения следующих операций над исходным кодом:
|
||||
|
||||
- Code-review.
|
||||
- Оценка качества кода.
|
||||
- Тестирование.
|
||||
|
||||
Возможна настройка модели приемки качества исходного кода, его проверки и тестирования.
|
||||
|
||||
Gitlab обладает интегрированными инструментами, позволяющими автоматизировать рутинные операции с исходным кодом:
|
||||
|
||||
- Непрерывную сборку и доставку (CI/CD).
|
||||
- Тестирование новых версий.
|
||||
- Релизный цикл продукта.
|
||||
- Проверки по безопасности.
|
||||
|
||||
Gitlab позволяет отслеживать и обрабатывать множество важной информации о процессе разработки:
|
||||
|
||||
- Трекинг затраченного рабочего времени на разработку функционала и выпуск новых релизов.
|
||||
- Мониторинг работоспособности приложения.
|
||||
- Расширенный сбор и анализ метрик.
|
||||
|
||||
## Часть 5. Основные команды git
|
||||
|
||||
### Команды Git
|
||||
|
||||
#### `git init`
|
||||
|
||||
Создание нового Git-репозитория (преобразование существующего проекта или создание нового пустого репозитория).
|
||||
|
||||
#### `git clone`
|
||||
|
||||
Создание локальной копии удаленного Git-репозитория.
|
||||
|
||||
#### `git branch`
|
||||
|
||||
Создание отдельной ветки на основе существующей для осуществления в новой ветке процесса разработки с возможным последующим слиянием.
|
||||
|
||||
#### `git checkout`
|
||||
|
||||
Переключение между различными версиями целевого объекта (файлы, коммиты и ветки).
|
||||
|
||||
#### `git status`
|
||||
|
||||
Отображение рабочего состояния файлов в локальной копии отслеживаемого проекта.
|
||||
|
||||
#### `git add`
|
||||
|
||||
Индексирование изменений в локальной копии отслеживаемого проекта.
|
||||
|
||||
#### `git commit`
|
||||
|
||||
Подготовка к отправке в удаленный репозиторий набора проиндексированных изменений в локальном проекте.
|
||||
|
||||
#### `git push`
|
||||
|
||||
Выгрузка в удаленный репозиторий подготовленного набора проиндексированных изменений из отслеживаемой локальной копии.
|
||||
|
||||
#### `git pull`
|
||||
|
||||
Загрузка содержимого из удаленного репозитория и немедленное слияние изменений в локальный отслеживаемый репозиторий.
|
||||
|
||||
#### `git restore`
|
||||
|
||||
Отмена индексации изменений в локальном репозитории.
|
||||
|
||||
#### `git revert`
|
||||
|
||||
Отмена подготовленных к отправке проиндексированных изменений в локальном репозитории с сохранением истории.
|
||||
|
||||
#### `git merge`
|
||||
|
||||
Слияние ответвления (ветки ответвленной с веткой изначальной). Изменения часто проходят процедуру согласования с участниками команды разработки через механизм merge Request.
|
||||
|
||||
#### `git rebase`
|
||||
|
||||
Операция, подобная слиянию (Git merge).
|
||||
|
||||
**Команды Git merge и Git rebase решают одну и ту же проблему –** **слияние одной ветки в другую, но делают это по-разному.**
|
||||
|
||||
**Git merge** — это неразрушающая операция, существующие ветки никак не изменяются.
|
||||
|
||||

|
||||
|
||||
**Git rebase** — это операция фактического перебазирования ответвленной ветки в исходную, с созданием идеальной линейной истории проекта.
|
||||
|
||||

|
||||
|
||||
Золотое правило перебазирования:
|
||||
|
||||
**“Never rebase while you're on a public branch”** (никогда не используйте git rebase в публичных репозиториях) Если вы предпочитаете иметь чистую линейную историю без ненужных коммитов слияния – используйте Git rebase.
|
||||
|
||||
Если вам необходимо сохранить полную историю проекта и избежать перезаписи публичных коммитов – используйте команду Git merge.
|
||||
@@ -0,0 +1,354 @@
|
||||
# МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ
|
||||
|
||||
Федеральное государственное автономное образовательное учреждение высшего образования
|
||||
|
||||
## НАЦИОНАЛЬНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ ЯДЕРНЫЙ УНИВЕРСИТЕТ «МИФИ»
|
||||
|
||||
*ИНСТИТУТ ИНТЕЛЛЕКТУАЛЬНЫХ КИБЕРНЕТИЧЕСКИХ СИСТЕМ*
|
||||
|
||||
КАФЕДРА КИБЕРНЕТИКИ
|
||||
|
||||
---
|
||||
|
||||
**Учебно-исследовательская работа на тему**
|
||||
|
||||
**«Обзор существующих решений и выбор инструментария для разработки системы учёта посещаемости студентов НИЯУ МИФИ»**
|
||||
|
||||
---
|
||||
|
||||
Выполнил студент: __________________________________
|
||||
|
||||
Группа: _______
|
||||
|
||||
Проверил: __________________________________
|
||||
|
||||
Оценка: ___________________
|
||||
|
||||
Дата: _____________________
|
||||
|
||||
Подпись: __________________
|
||||
|
||||
**Москва, 2025**
|
||||
|
||||
---
|
||||
|
||||
## Оглавление
|
||||
|
||||
- [Введение](#введение)
|
||||
- [1. Обзор существующих систем учёта посещаемости студентов](#1-обзор-существующих-систем-учёта-посещаемости-студентов)
|
||||
- [1.1. Российские системы](#11-российские-системы)
|
||||
- [1.2. Зарубежные системы](#12-зарубежные-системы)
|
||||
- [1.3. Сравнительный анализ аналогов](#13-сравнительный-анализ-аналогов)
|
||||
- [2. Анализ методов фиксации посещаемости](#2-анализ-методов-фиксации-посещаемости-студентов)
|
||||
- [3. Обзор существующих исследований](#3-обзор-существующих-исследований)
|
||||
- [4. Выбор технологического стека](#4-выбор-технологического-стека)
|
||||
- [Заключение](#заключение)
|
||||
- [Список использованной литературы](#список-использованной-литературы)
|
||||
|
||||
---
|
||||
|
||||
## Введение
|
||||
|
||||
Цифровая трансформация высшего образования является одним из ключевых направлений государственной политики Российской Федерации. Согласно федеральному проекту «Цифровая образовательная среда», к 2025 году не менее 85% российских вузов должны располагать современными цифровыми инструментами управления учебным процессом. Вместе с тем значительная часть университетов по-прежнему использует бумажные журналы или устаревшие информационные системы для фиксации посещаемости студентов, что порождает целый ряд организационных и технических проблем.
|
||||
|
||||
В современном техническом вузе, таком как НИЯУ МИФИ, где одновременно обучаются тысячи студентов, учёт посещаемости занятий является обязательной административной процедурой. Ручное ведение журналов посещаемости существенно увеличивает нагрузку на преподавателей, снижает оперативность получения статистики деканатами и не позволяет автоматически синхронизировать данные с внешними информационными системами университета. Проблема усугубляется тем, что НИЯУ МИФИ располагает развитой корпоративной IT-инфраструктурой, включающей Единую систему аутентификации (CAS), внутренние реестры академических данных и API расписания — однако существующие на рынке решения, как правило, не обеспечивают нативной интеграции с подобными системами.
|
||||
|
||||
Дополнительную актуальность проблеме придаёт требование прозрачности: студент должен иметь возможность самостоятельно видеть свою историю посещений, а не узнавать о накопленных пропусках в конце семестра. Преподаватель, в свою очередь, должен иметь возможность как автоматически фиксировать посещение (через QR-код), так и вручную корректировать данные при необходимости. Куратор группы и деканат должны иметь доступ к сводной статистике, а администратор системы — к управлению справочниками и периодами обучения.
|
||||
|
||||
Существующие на российском рынке продукты в области электронных журналов (БАРС.Образование, 1С:Университет, iSpring Learn) либо ориентированы на модель «преподаватель вручную отмечает студентов», либо представляют собой тяжёлые ERP-системы, предназначенные для комплексного управления вузом, а не для точечного решения задачи учёта посещаемости. Зарубежные корпоративные платформы (Microsoft Teams, Google Classroom, Brightspace) фиксируют посещаемость как побочный продукт онлайн-встреч и не применимы к очным занятиям. Специализированные QR-SaaS-решения предполагают хранение данных у стороннего провайдера, что недопустимо с точки зрения политики обработки персональных данных в государственном вузе.
|
||||
|
||||
Таким образом, ниша «веб-приложение для QR-самоотметки посещаемости с интеграцией в корпоративный SSO вуза, синхронизацией с внешними реестрами и полной ролевой моделью» остаётся незаполненной, что и обусловливает необходимость разработки собственного решения для НИЯУ МИФИ.
|
||||
|
||||
**Целью** настоящей работы является определение подхода к разработке веб-приложения для автоматизированного учёта посещаемости студентов НИЯУ МИФИ, обеспечивающего прозрачную фиксацию присутствия на занятиях, интеграцию с корпоративными системами университета и надёжную синхронизацию данных с внешними реестрами.
|
||||
|
||||
**Объект исследования** — процесс учёта посещаемости студентов в НИЯУ МИФИ, включающий взаимодействие студентов, преподавателей, кураторов и администраторов в рамках учебного процесса.
|
||||
|
||||
**Предмет исследования** — методы и инструменты автоматизации фиксации посещаемости студентов, архитектурные паттерны и технологии для реализации интегрированного корпоративного веб-приложения.
|
||||
|
||||
**Задачи работы:**
|
||||
|
||||
- провести обзор существующих аналогов систем учёта посещаемости (российских и зарубежных) и выявить их принципиальные ограничения применительно к условиям НИЯУ МИФИ;
|
||||
- проанализировать основные методы технической фиксации факта посещения студентом занятия и обосновать выбор QR-кодов как основного механизма самоотметки;
|
||||
- изучить актуальные научные исследования в области автоматизированного учёта посещаемости и смежных задач (архитектурные паттерны, производительность СУБД, фоновая обработка) для формирования теоретической базы разработки;
|
||||
- определить оптимальный технологический стек и архитектурные решения для разработки системы, обосновав выбор каждого компонента.
|
||||
|
||||
**Методологическую основу** исследования составляет системный анализ существующих программных решений и технологических подходов в области автоматизации учёта посещаемости в высших учебных заведениях. Исследование включает:
|
||||
|
||||
- сравнительный анализ коммерческих и открытых систем учёта посещаемости по шести критериям (QR-самоотметка, SSO-интеграция, Outbox-синхронизация, вузовская ролевая модель, on-premise-развёртывание, открытость кода);
|
||||
- сравнительный анализ методов технической фиксации посещаемости (ручная отметка, RFID/NFC, QR-коды, биометрия, геолокация) по критериям применимости в условиях МИФИ;
|
||||
- обзор рецензируемых научных публикаций (2015–2024) по теме QR-систем посещаемости, производительности СУБД, архитектурных паттернов для веб-приложений;
|
||||
- обоснование выбора технологического стека на основании совокупности технических и организационных критериев.
|
||||
|
||||
**Практическая значимость** работы заключается в создании обоснованной методической базы для разработки реального веб-приложения учёта посещаемости студентов НИЯУ МИФИ. Проведённый анализ аналогов и методов позволяет разработчикам осознанно подходить к выбору архитектурных решений, избегая типовых ошибок — таких как зависимость от стороннего SaaS-провайдера, отсутствие гарантий доставки данных во внешние реестры или невозможность интеграции с корпоративным SSO. Результаты работы могут быть использованы как основа для проектирования аналогичных систем в других технических вузах.
|
||||
|
||||
Работа включает введение, четыре раздела, заключение и список использованной литературы. В первом разделе проводится анализ аналогов — как российских, так и зарубежных систем учёта посещаемости. Во втором разделе рассматриваются основные технические методы фиксации посещаемости. Третий раздел посвящён обзору существующих научных исследований. Четвёртый раздел содержит обоснование выбора технологического стека.
|
||||
|
||||
---
|
||||
|
||||
## 1. Обзор существующих систем учёта посещаемости студентов
|
||||
|
||||
Прежде чем приступить к проектированию новой системы, необходимо провести детальный анализ уже существующих решений. Это позволит выявить незаполненную нишу, сформулировать требования к разрабатываемому приложению и избежать воспроизведения известных недостатков. В рамках настоящего раздела рассматриваются как российские (БАРС.Образование, 1С:Университет, iSpring Learn, Дневник.ру), так и зарубежные (Microsoft Teams, Google Classroom/Meet, Brightspace/D2L) системы, а также специализированные QR-SaaS-решения и открытые разработки.
|
||||
|
||||
### 1.1. Российские системы
|
||||
|
||||
**БАРС.Образование — Электронная школа (БАРС Груп).** Система является одной из наиболее распространённых платформ электронного журнала и дневника в Российской Федерации; входит в реестр отечественного ПО и рекомендована к тиражированию в рамках программы «Электронный регион» [9]. Основная функциональность: электронный журнал (оценки, темы уроков, планирование учебного процесса), электронный дневник, учёт посещаемости — вручную учителем, расписание, отчётность. Вход осуществляется через учётную запись Госуслуг.
|
||||
|
||||
Ключевые ограничения БАРС.Образование применительно к НИЯУ МИФИ состоят в следующем. Во-первых, архитектура системы построена на модели «школа — класс — родитель», которая не масштабируется на вузовскую структуру «поток — группа — подгруппа — кафедра — деканат». Во-вторых, посещаемость фиксируется исключительно ручным способом: преподаватель (учитель) отмечает отсутствующих в журнале; студенческий QR-self-service не предусмотрен. В-третьих, система не обеспечивает интеграции с корпоративным CAS-сервером вуза — идентификация пользователей осуществляется через Госуслуги/СМЭВ. В-четвёртых, не предусмотрен механизм гарантированной передачи данных о посещаемости во внешние академические реестры (Outbox-синхронизация). Таким образом, БАРС.Образование является решением для общеобразовательных школ и не применимо в условиях корпоративного технического университета.
|
||||
|
||||
**1С:Университет (фирма «1С»).** Это отраслевое решение на платформе «1С:Предприятие 8.3», предназначенное для комплексного управления учебным процессом вуза [10]. В его функциональность входят: управление контингентом студентов, ведение личных дел и зачётных книжек, учёт успеваемости и посещаемости, формирование ведомостей, приёмная кампания. Система поддерживает территориально распределённые базы данных и широко используется в российских вузах.
|
||||
|
||||
Несмотря на формальную поддержку учёта посещаемости, 1С:Университет обладает рядом принципиальных ограничений в контексте задач данной работы. Ввод данных о посещаемости осуществляется вручную через АРМ преподавателя — студенческий QR-self-service и нативный веб-интерфейс самообслуживания не предусмотрены. Система представляет собой монолитную проприетарную ERP-платформу, интеграция которой с внешними системами реализуется через специализированные механизмы обмена 1С, а не через REST API и корпоративный SSO. Развёртывание и настройка требуют значительных затрат времени и компетенций в области платформы 1С, что делает систему неподходящей для разработки адаптированного решения с нуля.
|
||||
|
||||
**iSpring Learn (iSpring Solutions).** Российская корпоративная LMS (система управления обучением), используемая клиентами в 172 странах. Платформа доступна как в облачном исполнении, так и в варианте развёртывания на серверах клиента [11]. Функциональность включает: курсы и тесты, вебинары (с интеграцией с Zoom), отчёт «ILT Attendance» по посещаемости очных тренингов/вебинаров (преподаватель отмечает участников вручную после мероприятия), SSO, открытый API, мобильное приложение.
|
||||
|
||||
Принципиальное ограничение iSpring Learn состоит в том, что система предназначена для корпоративного обучения персонала, а не для учёта очной посещаемости студентов в традиционном понимании. Посещаемость является побочной функцией модуля «мероприятий» (ILT — Instructor-Led Training): преподаватель вручную отмечает присутствовавших после завершения мероприятия. Отсутствует QR-самоотметка студентов на занятии, нет модели «учебный поток вуза», не предусмотрена Outbox-синхронизация с академическими реестрами. SSO в iSpring реализован через стандартный SAML/OAuth, но не через протокол CAS, используемый в МИФИ.
|
||||
|
||||
**Дневник.ру, ЭлЖур, Сетевой город. Образование (NetSchool).** Перечисленные системы относятся к категории школьных электронных журналов, ориентированных на базовый сценарий: ручная отметка посещаемости учителем, модель «класс/группа», работа с родителями. Ни одна из систем не поддерживает QR-самоотметку, корпоративный SSO вуза или вузовскую ролевую модель. Упоминаются здесь исключительно для полноты обзора российского рынка.
|
||||
|
||||
### 1.2. Зарубежные системы
|
||||
|
||||
**Microsoft Teams — функция учёта посещаемости.** Платформа Microsoft Teams поддерживает автоматическую генерацию отчёта о посещаемости онлайн-встречи: фиксируются имена участников, время входа/выхода, длительность участия, а также показатели вовлечённости (реакции, поднятые руки, включённые камеры) [12]. Отчёт доступен организатору встречи и экспортируется в CSV.
|
||||
|
||||
Ключевое ограничение Microsoft Teams заключается в том, что система фиксирует присутствие на видеовстрече, а не на очном занятии в аудитории. При численности участников более 120 человек отчёт в реальном времени содержит лишь частичный список; полный список формируется только после завершения встречи. Критически важно, что «If the event organizer leaves the org, reports are permanently deleted and can't be retrieved» — отчёты безвозвратно уничтожаются при увольнении организатора, что неприемлемо для академического архива. Нет QR-механизма для очных занятий, нет интеграции с CAS МИФИ, нет вузовской ролевой модели.
|
||||
|
||||
**Google Classroom / Google Meet.** В Google Classroom нативная функция отметки посещаемости отсутствует; типичный обходной путь — создание формы в Google Forms. Функциональность фиксации посещаемости в Google Meet доступна только на платных редакциях: Education Plus и Teaching and Learning Upgrade. По справке Google, «Attendance tracking is available to Google Workspace Essentials, Business Plus, Enterprise Starter, Enterprise Essentials, Enterprise Standard, Enterprise Plus, Education Plus and the Teaching and Learning Upgrade users» [13]. При этом формируемый отчёт относится к онлайн-встрече, а не к очному занятию.
|
||||
|
||||
**Brightspace (D2L) — инструмент Attendance.** Платформа Brightspace предоставляет базовый инструмент учёта посещаемости: реестры (registers), настраиваемые схемы статусов, порог предупреждения (Cause for Concern). Системная схема включает два статуса — present и absent; порог настраивается в процентах [14]. Преподаватель (или ассистент) отмечает студентов вручную — инструмент не является self-reporting. QR-самоотметка реализуется только через сторонние плагины (Qwickly Attendance, You-Attend) из D2L IntegrationHub, что означает дополнительные лицензионные затраты и зависимость от внешних провайдеров. Для внедрения системы потребуется развёртывание всей платформы Brightspace, что является избыточным для решения единственной задачи — учёта посещаемости.
|
||||
|
||||
**Специализированные QR/чек-ин SaaS-решения (OneTap, AccuClass, QR Attendee и аналоги).** Данный класс систем специально разработан для QR-фиксации посещаемости на мероприятиях и в учебных заведениях. Функциональность включает: генерацию QR-кодов, чек-ин студентов, импорт списков из Excel, аналитические дашборды, экспорт отчётов, иногда — геолокационную верификацию.
|
||||
|
||||
Несмотря на то что специализированные QR-SaaS-решения по набору функций ближе всего к требованиям данной работы, они обладают принципиальными ограничениями для применения в государственном университете. Данные хранятся в облаке стороннего провайдера, что не соответствует требованиям ФЗ-152 о локализации персональных данных и внутренней политике НИЯУ МИФИ. Отсутствует интеграция с CAS МИФИ — пользователи вынуждены создавать отдельные учётные записи. Нет механизма Outbox-синхронизации с внутренними академическими реестрами университета. Ролевая модель упрощена (обычно «организатор — участник») и не отражает вузовской иерархии. Закрытый исходный код исключает возможность адаптации под специфику МИФИ.
|
||||
|
||||
**Открытые проекты на GitHub (QR-Attendance-System, Check-by-QR и аналоги).** На платформе GitHub существует ряд прототипов QR-систем посещаемости, реализованных на различных стеках (Django, PHP/Bootstrap, Java/SQLite, .NET MAUI). Как правило, они поддерживают базовый сценарий: генерацию QR-кода, сканирование смартфоном, запись в базу. Некоторые включают геопроверку и защиту от повторной отметки. Однако все они являются студенческими или учебными прототипами: без промышленной надёжности, без гарантий доставки данных (Outbox), без CAS-интеграции, без полноценной ролевой модели.
|
||||
|
||||
### 1.3. Сравнительный анализ аналогов
|
||||
|
||||
На основании проведённого обзора составлена сравнительная таблица по шести ключевым критериям, определённым исходя из специфики задачи НИЯУ МИФИ.
|
||||
|
||||
| Система | QR-self-service | SSO/CAS | Outbox-синхр. | Ролевая модель вуза | On-premise | Open-source | Итог |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| БАРС.Образование | ✘ | ✘ | ✘ | Школьная | Зависит | ✘ | Не подходит |
|
||||
| 1С:Университет | ✘ | ✘ | ✘ | Частично | ✔ | ✘ | Не подходит |
|
||||
| iSpring Learn | ✘ | ✔ (SAML) | ✘ | Корпоративная | ✔ | ✘ | Не подходит |
|
||||
| MS Teams | ✘ | ✔ (Azure AD) | ✘ | ✘ | ✘ | ✘ | Не подходит |
|
||||
| Google Classroom | ✘ | ✔ (Google) | ✘ | ✘ | ✘ | ✘ | Не подходит |
|
||||
| Brightspace | Через плагин | ✔ | ✘ | LMS | ✔ | ✘ | Не подходит |
|
||||
| QR-SaaS (OneTap и др.) | ✔ | Частично | ✘ | ✘ | ✘ | ✘ | Не подходит |
|
||||
| **Проектируемая система МИФИ** | **✔** | **✔ (CAS)** | **✔** | **Полная** | **✔** | **✔** | **Целевая** |
|
||||
|
||||
*Таблица 1 — Сравнительная таблица систем учёта посещаемости*
|
||||
|
||||
Проведённый анализ демонстрирует, что ни один из рассмотренных продуктов одновременно не обеспечивает QR-самоотметку студента на очном занятии, интеграцию с корпоративным SSO на протоколе CAS, гарантированную Outbox-синхронизацию с внутренними академическими реестрами, полную вузовскую ролевую модель (студент / преподаватель / куратор / администратор) и возможность on-premise развёртывания с открытым исходным кодом. Это и составляет обоснование разработки собственного решения, адаптированного к специфике НИЯУ МИФИ.
|
||||
|
||||
---
|
||||
|
||||
## 2. Анализ методов фиксации посещаемости студентов
|
||||
|
||||
Для обоснования выбора QR-кодов в качестве основного механизма фиксации посещаемости необходимо рассмотреть весь спектр существующих технических методов и оценить их применимость в условиях НИЯУ МИФИ.
|
||||
|
||||
**Ручная отметка преподавателем** является традиционным и наиболее распространённым методом. Преподаватель вносит в бумажный или электронный журнал сведения об отсутствующих (или присутствующих). Метод не требует никаких технических средств, однако имеет существенные недостатки: трудоёмкость (особенно на больших потоках), субъективность, задержка актуализации данных (нередко — до окончания занятия или дня), отсутствие цифрового следа для немедленной синхронизации. В условиях МИФИ, где размер лекционного потока может достигать 150–200 человек, ручная отметка занимает до 10–15 минут полезного учебного времени.
|
||||
|
||||
**RFID/NFC-карты.** Студент прикладывает студенческий билет (или смартфон с NFC) к считывателю при входе в аудиторию. Метод обеспечивает высокую скорость фиксации и не требует активных действий студента (кроме поднесения карты). Ограничения: необходимость установки считывателей в каждой аудитории (существенные инвестиции в инфраструктуру), риск передачи карты другому лицу, зависимость от состояния физической инфраструктуры. Используется в ряде крупных российских вузов (в первую очередь — в корпусах с турникетами на проходных), однако не обеспечивает granular-учёта по конкретной паре/дисциплине без дополнительной привязки к расписанию.
|
||||
|
||||
**QR-коды.** Преподаватель генерирует QR-код для конкретного занятия (привязанный к дате, времени, дисциплине и аудитории), отображает его на экране. Студент сканирует код смартфоном — приложение записывает факт посещения. Метод сочетает преимущества автоматизации и минимальных инфраструктурных требований: считыватели не нужны, достаточно смартфона у студента. Защита от злоупотреблений обеспечивается ограниченным временем жизни кода (токен действителен N минут) и привязкой к учётной записи студента (один токен — одна отметка). Для МИФИ метод особенно удобен, поскольку все студенты располагают смартфонами, а CAS-аутентификация позволяет исключить анонимные отметки.
|
||||
|
||||
**Биометрическая идентификация** (распознавание лица, отпечатка пальца). Теоретически обеспечивает наивысшую достоверность: каждый студент идентифицируется по физическому признаку, что исключает передачу идентификатора другому лицу. Однако практическое применение в российском вузе сопряжено с рядом серьёзных ограничений. Обработка биометрических персональных данных (ПДн специальной категории) требует отдельного согласия субъекта, соответствия требованиям ФЗ-152 и подзаконных актов Роскомнадзора, что существенно усложняет юридическое оформление. Инфраструктурные затраты (видеокамеры с достаточным разрешением и освещением в каждой аудитории) значительны. Риск отказа в обслуживании при недостаточном качестве изображения неприемлем для повседневного учёта.
|
||||
|
||||
**Геолокация.** Приложение на смартфоне студента фиксирует его местоположение и сверяет с координатами аудитории. Применяется как дополнительный механизм верификации в ряде зарубежных систем. Ограничения в условиях МИФИ: многокорпусный кампус с корпусами, расположенными вплотную (GPS-точность 5–15 м недостаточна для различения соседних аудиторий), ограниченный GPS-сигнал внутри зданий, необходимость постоянного доступа к геолокации — что вызывает справедливые опасения пользователей в плане приватности.
|
||||
|
||||
На основании проведённого анализа сформирована сравнительная оценка методов:
|
||||
|
||||
| Метод | Инфраструктура | Скорость | Защита от подмены | Правовые риски | Применимость в МИФИ |
|
||||
|---|---|---|---|---|---|
|
||||
| Ручная отметка | Минимальная | Низкая | Низкая | Минимальные | Базовая (fallback) |
|
||||
| RFID/NFC | Высокая | Высокая | Средняя | Низкие | Ограниченная |
|
||||
| **QR-коды** | **Минимальная** | **Высокая** | **Средняя** | **Низкие** | **Оптимальная** |
|
||||
| Биометрия | Высокая | Высокая | Высокая | Высокие (ФЗ-152) | Не применима |
|
||||
| Геолокация | Минимальная | Высокая | Низкая | Средние | Вспомогательная |
|
||||
|
||||
*Таблица 2 — Сравнительная оценка методов фиксации посещаемости*
|
||||
|
||||
Таким образом, метод QR-кодов является оптимальным для НИЯУ МИФИ: он не требует специальной инфраструктуры в аудиториях, обеспечивает достаточную скорость отметки (5–10 секунд на студента), легко интегрируется с существующей CAS-аутентификацией и не создаёт правовых рисков, связанных с обработкой биометрических данных. Ручная отметка преподавателем сохраняется как резервный (fallback) механизм. Данный подход подтверждается результатами научных исследований, рассматриваемых в следующем разделе.
|
||||
|
||||
---
|
||||
|
||||
## 3. Обзор существующих исследований
|
||||
|
||||
Анализ актуальных научных публикаций по теме автоматизации учёта посещаемости и смежным техническим задачам позволяет сформировать теоретическую базу для проектирования системы и избежать ранее выявленных проблем. Ниже рассматриваются шесть работ, непосредственно или косвенно связанных с темой настоящего исследования.
|
||||
|
||||
### *Nuhi A., Memeti A., Imeri F., Çiço B. «Smart Attendance System using QR Code»*
|
||||
|
||||
Авторы представили разработку автоматизированной системы учёта посещаемости студентов на основе QR-кодов для применения в учебных заведениях [2]. Система реализована в виде веб-приложения: преподаватель генерирует динамический QR-код для конкретного занятия, студент сканирует его смартфоном, сервер фиксирует отметку посещения с привязкой к учётной записи. Защита от злоупотреблений обеспечивается ограниченным временем действия кода и механизмом однократного использования токена.
|
||||
|
||||
К достоинствам предложенного подхода авторы относят: минимальные требования к инфраструктуре (не требуется специальное оборудование в аудиториях), высокую скорость отметки, возможность offline-генерации кодов при нестабильном соединении. Проведённые испытания подтвердили работоспособность системы в аудиторных условиях.
|
||||
|
||||
Ограничения работы связаны с отсутствием интеграции с корпоративными системами аутентификации и реестрами вуза: система функционирует автономно, что затрудняет синхронизацию с учебными планами и внешними базами данных академической успеваемости. Авторы также не рассматривают механизмы надёжной доставки данных во внешние системы и не предоставляют анализа архитектурных паттернов для промышленного развёртывания.
|
||||
|
||||
Результаты данного исследования подтверждают принципиальную применимость QR-метода для учёта посещаемости в высших учебных заведениях и служат теоретической базой для выбора механизма самоотметки в проектируемой системе МИФИ. Выявленные ограничения — отсутствие SSO-интеграции и синхронизации с реестрами — непосредственно учтены при постановке задачи настоящей дипломной работы.
|
||||
|
||||
### *Глуховский К.С., Пирожков Р.В., Цвелик Е.А. «Электронный журнал как элемент цифровой трансформации вуза»*
|
||||
|
||||
Работа посвящена опыту разработки и внедрения электронного журнала посещаемости и академической успеваемости в качестве модуля Электронной информационно-образовательной среды (ЭИОС) в Волгодонском инженерно-техническом институте НИЯУ МИФИ [3]. Система реализована как часть единой цифровой среды вуза и интегрирована с корпоративными справочниками.
|
||||
|
||||
Авторы подробно описывают процесс импорта академических данных (расписание, группы, студенты, дисциплины) из центральных систем вуза, структуру ролей пользователей (студент, преподаватель, деканат, администратор), механизм формирования отчётов по посещаемости. Работа имеет особое значение для настоящего исследования как отраслевой аналог из структуры НИЯУ МИФИ, демонстрирующий типовые задачи (импорт данных расписания, разграничение ролей, отчётность для деканата) применительно к инфраструктуре университета.
|
||||
|
||||
Ограничения описанного решения: не предусмотрена студенческая QR-самоотметка (посещаемость фиксируется преподавателем); отсутствует описание механизма синхронизации с внешними реестрами и гарантий доставки данных. Выявленные недостатки конкретизируют требования к проектируемой системе МИФИ в части добавления QR-механизма и реализации Outbox-паттерна.
|
||||
|
||||
### *Salunke S.V., Ouda A. «A Performance Benchmark for the PostgreSQL and MySQL Databases»*
|
||||
|
||||
В работе проводится сравнительное нагрузочное тестирование СУБД PostgreSQL и MySQL по операциям INSERT, SELECT (с/без WHERE) и UPDATE на наборах данных объёмом до 1 миллиона записей [5]. Эксперименты выполнены в идентичных условиях аппаратного и программного обеспечения.
|
||||
|
||||
Ключевые результаты: на операциях INSERT для 1 миллиона записей PostgreSQL показал время выполнения 0,6–0,8 мс, MySQL — 9–12 мс (в 13–15 раз медленнее). На операциях SELECT с фильтром WHERE PostgreSQL продемонстрировал время 0,09–0,13 мс против 0,9–1 мс для MySQL (примерно в 9 раз быстрее).
|
||||
|
||||
Авторы честно оговаривают ограничения: на простых OLTP SELECT при небольших объёмах данных и оптимизированных индексах разница может быть менее выражена; результаты зависят от профиля нагрузки. Тем не менее для задач, требующих сложных транзакций (Outbox-запись, ACID-гарантии при одновременной работе сотен студентов) и богатства типов данных (JSONB, массивы), PostgreSQL является обоснованным выбором. Данное исследование используется в разделе 4 для обоснования выбора PostgreSQL в качестве СУБД проектируемой системы.
|
||||
|
||||
### *Richardson C. «Microservices Patterns: With Examples in Java»*
|
||||
|
||||
Монография Криса Ричардсона является фундаментальным изданием в области архитектурных паттернов для распределённых систем [4]. Глава 3 посвящена паттерну Transactional Outbox, решающему проблему «двойной записи» (dual write) — несогласованности данных при одновременной записи в базу данных и отправке сообщения внешней системе.
|
||||
|
||||
Суть паттерна: при выполнении бизнес-операции (например, фиксации посещения) событие записывается в специальную таблицу-«почтовый ящик» (outbox) в той же атомарной транзакции, что и основные данные. Отдельный процесс-релей (publisher) асинхронно считывает необработанные записи из outbox-таблицы и передаёт их во внешние системы. При сбое релей повторяет попытку (гарантия at-least-once); получатели должны быть идемпотентны. Паттерн устраняет необходимость в распределённых транзакциях (2PC) и гарантирует, что ни одно событие не будет потеряно даже при временной недоступности внешней системы.
|
||||
|
||||
Ограничение паттерна: гарантия at-least-once предполагает, что одно и то же событие может быть доставлено более одного раза; получатель обязан обеспечить идемпотентную обработку. Для проектируемой системы МИФИ это означает, что при проектировании API внешних реестров следует предусмотреть механизм дедупликации по идентификатору события.
|
||||
|
||||
Данная работа является первичным теоретическим источником для обоснования архитектурного решения, реализованного в проектируемой системе через таблицу `update_attendance_students_outbox` и фоновый воркер `LessonVisitSyncWorker`.
|
||||
|
||||
### *Методика автоматизации контроля посещаемости очных занятий по опыту кафедры информационных компьютерных технологий*
|
||||
|
||||
Данная работа, опубликованная в открытом доступе на платформе КиберЛенинка, описывает практический опыт кафедры ИКТ российского технического вуза по автоматизации контроля посещаемости очных занятий. Авторы рассматривают переход от бумажных журналов к цифровому учёту с использованием доступных инструментов: Google-таблицы в связке с QR-сканерами.
|
||||
|
||||
Ключевые наблюдения авторов, актуальные для настоящей работы: проблема автоматизации наиболее остро проявляется на крупных потоках (100+ студентов), где ручная отметка занимает значительное время; преподаватели позитивно оценивают делегирование процедуры отметки самим студентам (самоотметку). Авторы также отмечают необходимость интеграции с корпоративными справочниками студентов и расписанием для исключения ошибок при ручном вводе данных.
|
||||
|
||||
Ограничение: использование Google-таблиц предполагает хранение данных у иностранного поставщика, что создаёт риски соответствия требованиям ФЗ-152 о локализации персональных данных. В проектируемой системе МИФИ аналогичный функционал реализуется на собственной инфраструктуре вуза с развёртыванием через Capistrano.
|
||||
|
||||
### *AWS Prescriptive Guidance. «Transactional Outbox Pattern»*
|
||||
|
||||
Руководство по архитектурным паттернам Amazon Web Services детально описывает транзакционный Outbox-паттерн применительно к cloud-native приложениям [15]. Документ формализует компоненты паттерна: бизнес-сервис (атомарно пишет в БД и outbox), relayer (опрашивает outbox и публикует события), event consumer (обрабатывает события идемпотентно).
|
||||
|
||||
В документе подчёркивается, что данный паттерн решает фундаментальную проблему надёжности в микросервисных и распределённых системах: невозможность атомарно выполнить запись в базу данных и отправку сообщения брокеру без использования двухфазной фиксации (2PC), которая неприменима в современных высоконагруженных системах из-за сложности реализации и низкой производительности.
|
||||
|
||||
Рекомендации AWS по реализации паттерна учтены при проектировании модуля синхронизации проектируемой системы: таблица outbox включает поля `id` (уникальный идентификатор события), `payload` (JSON с данными посещения), `status` (новое/отправлено), `created_at`, `sent_at`. Воркер периодически запрашивает записи со статусом «новое», отправляет их в целевую систему и обновляет статус. При сбое статус остаётся «новое» и событие будет повторно обработано на следующем цикле.
|
||||
|
||||
---
|
||||
|
||||
## 4. Выбор технологического стека
|
||||
|
||||
Выбор оптимального технологического стека для разработки системы учёта посещаемости представляет собой комплексную инженерную задачу, требующую оценки каждого компонента по критериям производительности, зрелости экосистемы, соответствия задаче и скорости разработки. В данном разделе проводится сравнительный анализ альтернативных технологий и обосновывается итоговый выбор.
|
||||
|
||||
### 4.1. Веб-фреймворк: Ruby on Rails 7
|
||||
|
||||
Для реализации бэкенда рассматривались четыре ведущих веб-фреймворка: Ruby on Rails (Ruby), Django (Python), Laravel (PHP) и Spring Boot (Java).
|
||||
|
||||
**Django (Python)** обеспечивает высокую производительность Python-рантайма, богатую экосистему библиотек для науки о данных и зрелую ORM (Django ORM). Основным ограничением для данной задачи является отсутствие встроенного, полноценно интегрированного механизма для server-side real-time обновлений интерфейса (аналога Hotwire в Rails): подобный функционал требует использования Django Channels (отдельный компонент, значительно усложняющий стек) или SPA-фронтенда.
|
||||
|
||||
**Laravel (PHP)** демонстрирует высокую скорость разработки CRUD-приложений благодаря выразительному синтаксису и богатой экосистеме. Livewire — аналог Hotwire для Laravel — позволяет создавать реактивные интерфейсы без SPA. Однако PHP традиционно требует настройки веб-сервера (Apache/Nginx + PHP-FPM), что усложняет процедуру деплоя по сравнению с Rails+Capistrano. Кроме того, в существующей инфраструктуре проекта уже использован стек Ruby.
|
||||
|
||||
**Spring Boot (Java)** является стандартом корпоративной разработки на JVM: строгая типизация, высокая производительность, зрелая экосистема. Ограничения: значительный объём boilerplate-кода при реализации CRUD-операций, медленный цикл разработки и тестирования по сравнению с Ruby/Python, отсутствие нативного аналога Hotwire.
|
||||
|
||||
**Ruby on Rails 7** реализует строгий паттерн MVC, принципы Convention over Configuration и DRY, что ускоряет разработку типовых CRUD-компонентов (CRUD занятий, групп, студентов) и снижает объём шаблонного кода. Начиная с версии 7.0, Hotwire (Turbo + Stimulus) интегрирован в Rails «из коробки» [1], что позволяет реализовать отзывчивый интерфейс без выделенного SPA-фронтенда. Зрелая экосистема gem-пакетов (`rack-cas`, `pagy`, `paper_trail`, `rqrcode`, `httparty`) обеспечивает готовые реализации всех необходимых функций без написания дополнительного кода. Rails используется в production крупнейшими технологическими компаниями (GitHub, Shopify, Basecamp), что подтверждает его пригодность для промышленного применения.
|
||||
|
||||
*Честный контраргумент:* Ruby как язык уступает по «сырой» вычислительной производительности оптимизированному Python/Java; MRI-интерпретатор Ruby использует GIL (Global Interpreter Lock), ограничивая параллелизм в рамках одного процесса. Эти ограничения компенсируются фоновой обработкой тяжёлых задач через Sidekiq (многопоточная модель на уровне воркеров), кэшированием и горизонтальным масштабированием приложения.
|
||||
|
||||
### 4.2. Фронтенд: Hotwire (Turbo + Stimulus)
|
||||
|
||||
Hotwire («HTML Over The Wire») — это набор технологий для создания интерактивных веб-интерфейсов без использования полноценного SPA-фреймворка [6]. Концепция: вместо того чтобы передавать JSON и перестраивать DOM на клиенте (React/Vue), сервер возвращает готовые HTML-фрагменты, которые встраиваются в страницу.
|
||||
|
||||
Состав Hotwire:
|
||||
- **Turbo Drive** — навигация по страницам без полной перезагрузки (аналог pjax);
|
||||
- **Turbo Frames** — изолированные обновляемые области страницы (загрузка фрагмента без перезагрузки всего layout);
|
||||
- **Turbo Streams** — точечные DOM-операции (append, prepend, replace, remove), в том числе через WebSocket для realtime-обновлений;
|
||||
- **Stimulus** — минималистичный JS-фреймворк для добавления поведения через data-атрибуты без перестройки архитектуры.
|
||||
|
||||
Для проектируемой системы учёта посещаемости Hotwire особенно подходит по следующим причинам. Задача не требует сложного клиентского состояния (не является полноценным SPA): основные операции — загрузка списка присутствующих, отметка студента, обновление строки — идеально ложатся на модель Turbo Frames/Streams. Backend-ориентированная команда разработки не обязана владеть React/TypeScript. Кодовая база остаётся монолитной, что упрощает поддержку и деплой.
|
||||
|
||||
*Ограничение:* для интерфейсов с чрезвычайно высокой интерактивностью и сложным клиентским состоянием (например, drag-and-drop конструкторы, сложные формы с зависимыми полями) Hotwire менее удобен, чем React. В контексте данного проекта таких требований нет.
|
||||
|
||||
### 4.3. СУБД: PostgreSQL
|
||||
|
||||
PostgreSQL — полнофункциональная объектно-реляционная СУБД с открытым исходным кодом, поддерживающая ACID-транзакции через механизм MVCC (Multiversion Concurrency Control) [8]. Для проектируемой системы рассматривались PostgreSQL и MySQL.
|
||||
|
||||
По результатам сравнительного бенчмарка Salunke & Ouda (2024) [5], PostgreSQL демонстрирует превосходство в транзакционных операциях: на 1 миллионе записей INSERT выполняется в 13–15 раз быстрее, SELECT с WHERE — в 9 раз быстрее, чем в MySQL. Для системы учёта посещаемости, где сотни студентов одновременно фиксируют посещение (пиковая нагрузка в начале лекции), высокая производительность параллельных INSERT критически важна.
|
||||
|
||||
Дополнительные аргументы в пользу PostgreSQL: поддержка типа JSONB (используется при хранении метаданных Outbox-событий), строгая типизация и богатство встроенных типов, развитая система ролей и схем (что соответствует многоролевой архитектуре системы), логическая репликация для потенциального горизонтального масштабирования, нативная поддержка полнотекстового поиска.
|
||||
|
||||
*Оговорка:* на простых OLTP-запросах с небольшими объёмами данных и оптимизированными индексами MySQL может демонстрировать сопоставимую производительность. Выбор PostgreSQL обусловлен прежде всего транзакционной целостностью (критичной для Outbox-паттерна) и богатством типов данных, а не исключительно скоростью.
|
||||
|
||||
### 4.4. Фоновые задачи: Sidekiq + Redis
|
||||
|
||||
Sidekiq — многопоточный обработчик фоновых задач для Ruby, использующий Redis как хранилище очередей [7]. Выбор Sidekiq обусловлен несколькими факторами: тесная интеграция с Rails через Active Job (переключение адаптера одной строкой конфигурации), многопоточная модель (эффективнее по памяти, чем процессные аналоги Resque и Delayed Job), встроенные очереди с приоритетами, автоматические повторы с экспоненциальной задержкой, веб-интерфейс мониторинга Sidekiq Web UI.
|
||||
|
||||
В проектируемой системе Sidekiq решает три критически важные задачи:
|
||||
1. **Реализация Outbox-релея:** воркер `LessonVisitSyncWorker` периодически (через Sidekiq Scheduler) опрашивает outbox-таблицу и отправляет необработанные события в целевые системы.
|
||||
2. **Тяжёлые импорты:** синхронизация расписания, групп и студентов из API МИФИ выполняется в фоне без блокировки основного веб-процесса.
|
||||
3. **Генерация отчётов** по запросу деканата.
|
||||
|
||||
Redis выполняет двойную роль: хранилище очередей для Sidekiq и кэш приложения (кэширование часто запрашиваемых данных расписания, справочников).
|
||||
|
||||
### 4.5. Аутентификация: CAS (Central Authentication Service)
|
||||
|
||||
Central Authentication Service (CAS) — протокол единого входа (SSO), разработанный в Йельском университете и поддерживаемый Apereo Foundation [16]. При аутентификации через CAS приложение перенаправляет пользователя на центральный CAS-сервер; пользователь вводит логин/пароль там, не передавая их приложению. После успешной аутентификации CAS выдаёт одноразовый service ticket, который приложение валидирует через защищённый канал. Приложение не хранит пароли пользователей.
|
||||
|
||||
CAS является стандартом SSO в университетской среде: его используют сотни ведущих вузов мира. В НИЯУ МИФИ CAS обеспечивает единый вход в корпоративные сервисы — от почты до личного кабинета. Интеграция проектируемой системы с CAS МИФИ через gem `rack-cas` позволяет использовать существующие учётные записи студентов и преподавателей без создания дополнительных паролей.
|
||||
|
||||
### 4.6. Архитектурные паттерны
|
||||
|
||||
**Service Objects.** Паттерн предполагает вынос сложной бизнес-логики из моделей и контроллеров в специализированные объекты (PORO — plain old Ruby objects), размещаемые в `app/services`. Каждый сервис отвечает за одну операцию (принцип единственной ответственности) и имеет единственный публичный метод `call`. Применяется для: `ImportStudentsService` (импорт студентов из API МИФИ), `ImportLessonsService` (импорт расписания), `CreateLessonVisitService` (логика фиксации посещения). Преимущества: тестируемость в изоляции, чёткое разделение ответственности, «тонкие» модели и контроллеры.
|
||||
|
||||
**Namespaced Controllers.** Разделение контроллеров по пространствам имён (`Admin::`, `Moderator::`, `Student::`) изолирует ролевые интерфейсы, упрощает маршрутизацию (`resources :lessons` вложены в соответствующий namespace) и авторизацию (`before_action :require_admin` в базовом контроллере пространства имён). Паттерн естественно поддерживает вузовскую ролевую модель (студент / преподаватель / куратор / администратор).
|
||||
|
||||
**Outbox Pattern.** Как описано в разделе 3 со ссылкой на Richardson [4] и AWS [15], паттерн Transactional Outbox обеспечивает гарантию at-least-once для доставки событий посещаемости во внешние реестры МИФИ. Реализован через таблицу `update_attendance_students_outbox` и фоновый воркер `LessonVisitSyncWorker` (Sidekiq), периодически публикующий необработанные записи.
|
||||
|
||||
### 4.7. CSS: Tailwind CSS
|
||||
|
||||
Tailwind CSS — utility-first CSS-фреймворк, интегрированный в Rails 7 через gem `tailwindcss-rails` [17]. Подход «utility-first» предполагает описание стилей непосредственно в HTML-разметке через семантические классы (`flex`, `p-4`, `text-gray-700`). Ключевые преимущества для данного проекта: существенно меньший production-бандл за счёт JIT-purge (удаляются все неиспользуемые классы); полная кастомизация через `tailwind.config.js` без переопределения базовых стилей фреймворка; нативная интеграция с Rails 7 (`rails new myapp --css tailwind`).
|
||||
|
||||
Bootstrap 5 используется для отдельных UI-компонентов, где применение готовых составных элементов (модальные окна, дропдауны) более уместно. Такое сочетание позволяет использовать сильные стороны обоих инструментов, что соответствует практике итеративной разработки.
|
||||
|
||||
### 4.8. Деплой: Capistrano + Mise
|
||||
|
||||
Capistrano — инструмент автоматизации деплоя для Ruby-приложений, реализующий стратегию rolling deployment с сохранением предыдущих релизов [19]. Поддерживаются окружения staging и production; деплой сводится к одной команде `cap production deploy`. Mise — менеджер версий рантаймов (Ruby, Node.js), обеспечивающий идентичность версий между локальной разработкой, CI и production-сервером [20].
|
||||
|
||||
---
|
||||
|
||||
## Заключение
|
||||
|
||||
В ходе настоящей учебно-исследовательской работы проведён комплексный анализ существующих систем и методов учёта посещаемости студентов, а также обоснован технологический стек для разработки корпоративного веб-приложения для НИЯУ МИФИ.
|
||||
|
||||
По результатам обзора восьми российских и зарубежных систем (БАРС.Образование, 1С:Университет, iSpring Learn, Microsoft Teams, Google Classroom, Brightspace, QR-SaaS и открытые проекты) установлено, что ни одна из рассмотренных систем не обеспечивает одновременно QR-самоотметку студента на очном занятии, интеграцию с корпоративным SSO на протоколе CAS, гарантированную Outbox-синхронизацию с внутренними академическими реестрами и полную вузовскую ролевую модель. Данный вывод обосновывает необходимость разработки собственного решения, адаптированного к специфике инфраструктуры НИЯУ МИФИ.
|
||||
|
||||
Из пяти рассмотренных методов технической фиксации посещаемости (ручная отметка, RFID/NFC, QR-коды, биометрия, геолокация) оптимальным для условий МИФИ признан метод QR-кодов. Он сочетает минимальные инфраструктурные требования (не требует оборудования в аудиториях), высокую скорость отметки, приемлемый уровень защиты от подмены через механизм одноразовых токенов и отсутствие правовых рисков, связанных с обработкой биометрических данных.
|
||||
|
||||
Обзор шести актуальных научных публикаций подтвердил применимость QR-подхода для учёта посещаемости (Nuhi et al., 2020), выявил типовые задачи, стоящие перед вузами при разработке подобных систем в контексте НИЯУ МИФИ (Глуховский и др., 2021), обосновал выбор PostgreSQL как СУБД для высококонкурентных транзакционных операций (Salunke & Ouda, 2024) и сформировал теоретическую базу для реализации Outbox-паттерна (Richardson, 2018; AWS Prescriptive Guidance).
|
||||
|
||||
Определён следующий технологический стек: Ruby on Rails 7 (бэкенд, MVC, Convention over Configuration), Hotwire/Turbo/Stimulus (фронтенд без SPA), PostgreSQL (СУБД с ACID-транзакциями и поддержкой JSONB), Redis + Sidekiq (фоновые задачи и Outbox-релей), rack-cas (интеграция с CAS МИФИ), Tailwind CSS + Bootstrap (стилизация). Архитектурные паттерны: Service Objects (изоляция бизнес-логики), Namespaced Controllers (ролевое разделение), Transactional Outbox (надёжная синхронизация), MVC (структурирование приложения). Деплой — через Capistrano + Mise.
|
||||
|
||||
Результаты работы формируют обоснованную теоретическую базу для реализации системы в рамках дипломной работы и могут служить методической основой для разработки аналогичных систем в других технических вузах с корпоративной IT-инфраструктурой на основе протокола CAS.
|
||||
|
||||
---
|
||||
|
||||
## Список использованной литературы
|
||||
|
||||
1. Ruby on Rails Guides: Getting Started with Rails // Ruby on Rails Documentation, 2024. URL: https://guides.rubyonrails.org/ (дата обращения: 20.05.2025).
|
||||
2. Nuhi A., Memeti A., Imeri F., Çiço B. Smart Attendance System using QR Code // 2020 9th Mediterranean Conference on Embedded Computing (MECO), Budva, Montenegro, 2020, pp. 1–4. DOI: 10.1109/MECO49872.2020.9134225.
|
||||
3. Глуховский К.С., Пирожков Р.В., Цвелик Е.А. Электронный журнал как элемент цифровой трансформации вуза // Инженерный вестник Дона. 2021. № 5. URL: https://ivdon.ru (дата обращения: 22.05.2025).
|
||||
4. Richardson C. Microservices Patterns: With Examples in Java. Manning Publications, 2018. 520 с.
|
||||
5. Salunke S.V., Ouda A. A Performance Benchmark for the PostgreSQL and MySQL Databases // Future Internet. 2024. Vol. 16, No. 10. Art. 382. DOI: 10.3390/fi16100382.
|
||||
6. Hotwire: HTML Over The Wire // Hotwire Documentation. URL: https://hotwired.dev/ (дата обращения: 20.05.2025).
|
||||
7. Sidekiq: Simple, Efficient Background Processing for Ruby // Sidekiq Documentation. URL: https://sidekiq.org/ (дата обращения: 20.05.2025).
|
||||
8. PostgreSQL: The World's Most Advanced Open Source Relational Database // PostgreSQL 16 Documentation. URL: https://www.postgresql.org/docs/16/ (дата обращения: 20.05.2025).
|
||||
9. БАРС.Образование — Электронная школа // БАРС Груп. URL: https://bars.group/products/education/ (дата обращения: 15.05.2025).
|
||||
10. 1С:Университет — Возможности // 1С Образование. URL: https://solutions.1c.ru/catalog/university/features (дата обращения: 15.05.2025).
|
||||
11. iSpring Learn: корпоративная платформа для обучения // iSpring Solutions. URL: https://www.ispring.ru/ispring-learn (дата обращения: 15.05.2025).
|
||||
12. Manage the Attendance and Engagement Report for Meetings and Events in Microsoft Teams // Microsoft Learn. URL: https://learn.microsoft.com/en-us/microsoftteams/teams-analytics-and-reports/meeting-attendance-report (дата обращения: 16.05.2025).
|
||||
13. Track Attendance & View Live Stream Report // Google Classroom Help. URL: https://support.google.com/edu/classroom/answer/10090454 (дата обращения: 16.05.2025).
|
||||
14. Brightspace by D2L: Attendance Tool // D2L Documentation. URL: https://documentation.brightspace.com/ (дата обращения: 16.05.2025).
|
||||
15. Transactional Outbox Pattern // AWS Prescriptive Guidance. URL: https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/transactional-outbox.html (дата обращения: 18.05.2025).
|
||||
16. CAS Protocol Specification // Apereo Foundation. URL: https://apereo.github.io/cas/ (дата обращения: 18.05.2025).
|
||||
17. Tailwind CSS Documentation // Official Site. URL: https://tailwindcss.com/docs/ (дата обращения: 20.05.2025).
|
||||
18. Bootstrap 5 Documentation // Official Site. URL: https://getbootstrap.com/docs/5.3/ (дата обращения: 20.05.2025).
|
||||
19. Capistrano: Remote Server Automation and Deployment Tool // Official Documentation. URL: https://capistranorb.com/ (дата обращения: 20.05.2025).
|
||||
20. Mise: Polyglot Runtime Manager // Official Documentation. URL: https://mise.jdx.dev/ (дата обращения: 20.05.2025).
|
||||
21. Методика автоматизации контроля посещаемости очных занятий по опыту кафедры информационных компьютерных технологий // КиберЛенинка. URL: https://cyberleninka.ru/article/n/metodika-avtomatizatsii-kontrolya-poseschaemosti-ochnyh-zanyatiy-po-opytu-kafedry-informatsionnyh-kompyuternyh-tehnologiy (дата обращения: 22.05.2025).
|
||||
22. Pagy: Fast, Ultralight, Agnostic, Extendable Pagination for Ruby // GitHub. URL: https://github.com/ddnexus/pagy (дата обращения: 21.05.2025).
|
||||
23. Paper Trail: Track Changes to your Rails Models // GitHub. URL: https://github.com/paper-trail-gem/paper_trail (дата обращения: 21.05.2025).
|
||||
24. rqrcode: A Ruby Library for Encoding QR Codes // GitHub. URL: https://github.com/whomwah/rqrcode (дата обращения: 21.05.2025).
|
||||
25. HTTParty: Makes HTTP Fun Again // GitHub. URL: https://github.com/jnunemaker/httparty (дата обращения: 21.05.2025).
|
||||
@@ -0,0 +1,249 @@
|
||||
---
|
||||
title: "УИР: Обзор систем учёта посещаемости студентов НИЯУ МИФИ"
|
||||
status: stable
|
||||
type: document
|
||||
tags:
|
||||
- уир
|
||||
- мифи
|
||||
- разработка
|
||||
created: 2026-06-08
|
||||
updated: 2026-06-14
|
||||
---
|
||||
|
||||
# УИР\_Сводный\_вариант
|
||||
|
||||
## Введение
|
||||
|
||||
Цифровизация управления учебным процессом требует от университета не только электронного хранения данных, но и прозрачной, проверяемой фиксации факта присутствия студентов на занятиях. Согласно исследованию [\[Электронный журнал как элемент цифровой трансформации вуза\]](https://ivdon.ru/back_media/uploads/article/pdf/IVD_47__4_glukhovsky_pirozhkov_tsvelik.pdf_048169e65f.pdf) средний балл студента обратно зависит от процента пропущенных им занятий. В крупном техническом вузе, традиционные бумажные журналы и разрозненные электронные таблицы создают существенные организационные ограничения: увеличивают нагрузку на преподавателей, повышают риск потери или несвоевременного внесения данных, затрудняют оперативное получение статистики деканатами и не позволяют автоматически синхронизировать сведения с внутренними информационными системами университета.
|
||||
|
||||
Проблема особенно актуальна для НИЯУ МИФИ, поскольку университет располагает развитой корпоративной IT-инфраструктурой: единой системой аутентификации, внутренними академическими реестрами, расписанием и ролевыми моделями доступа. Готовая система учёта посещаемости для такого окружения должна не просто фиксировать отметку, а работать как часть корпоративного контура: использовать существующие учётные записи, корректно разделять роли студентов, преподавателей, кураторов, деканата и администраторов, а также гарантированно передавать события посещаемости во внешние реестры.
|
||||
|
||||
Дополнительную актуальность задаче придаёт требование прозрачности:
|
||||
1) студент должен иметь возможность видеть собственную историю посещений до окончания семестра;
|
||||
2) преподаватель должен иметь быстрый механизм фиксации присутствия и возможность ручной корректировки спорных случаев;
|
||||
3) куратор и деканат должны получать сводную аналитику без ручной консолидации журналов.
|
||||
|
||||
Проведённый анализ показывает, что массовые российские и зарубежные решения не сочетают одновременно ключевые свойства, необходимые корпоративному вузу: QR-самоотметку студента на очном занятии, интеграцию с корпоративным SSO на базе CAS, гарантированную синхронизацию с внутренними реестрами и полноценную вузовскую ролевую модель. Это обосновывает необходимость разработки собственной системы.
|
||||
|
||||
**Целью** настоящей работы является определение подхода к разработке веб-приложения для автоматизированного учёта посещаемости студентов НИЯУ МИФИ, обеспечивающего прозрачную фиксацию присутствия на занятиях, интеграцию с корпоративными системами университета и надёжную синхронизацию данных с внешними реестрами.
|
||||
|
||||
**Объект исследования** — процесс учёта посещаемости студентов в НИЯУ МИФИ, включающий взаимодействие студентов, преподавателей, кураторов, деканата и администраторов в рамках учебного процесса.
|
||||
|
||||
**Предмет исследования** — методы и инструменты автоматизации фиксации посещаемости студентов, архитектурные паттерны и технологии для реализации интегрированного корпоративного веб-приложения.
|
||||
|
||||
**Задачи работы:**
|
||||
|
||||
1. Провести обзор существующих аналогов систем учёта посещаемости и выявить их ограничения применительно к условиям НИЯУ МИФИ.
|
||||
2. Проанализировать основные методы технической фиксации факта посещения студентом занятия.
|
||||
3. Обосновать выбор QR-кодов как основного механизма самоотметки.
|
||||
4. Изучить научные и отраслевые источники по автоматизированному учёту посещаемости, производительности СУБД и архитектурным паттернам надёжной синхронизации.
|
||||
5. Определить технологический стек и архитектурные решения для разработки системы.
|
||||
|
||||
**Методологическую основу** исследования составляет системный анализ существующих программных решений и технологических подходов в области автоматизации учёта посещаемости. В работе используются сравнительный анализ коммерческих и открытых систем, анализ методов фиксации посещаемости, обзор научных публикаций и проверка технических решений по официальной документации.
|
||||
|
||||
**Практическая значимость** работы заключается в формировании обоснованной методической базы для последующей разработки собственной системы. Результаты анализа позволяют избежать типовых ошибок: зависимости от внешнего SaaS-провайдера, отсутствия гарантий доставки данных, невозможности интеграции с корпоративным SSO и смешения ролей пользователей.
|
||||
|
||||
## 1. Обзор существующих систем учёта посещаемости студентов
|
||||
|
||||
Перед проектированием новой системы необходимо оценить существующие решения: российские электронные журналы и ERP-платформы, зарубежные LMS и сервисы видеоконференций, специализированные QR/SaaS-сервисы и открытые прототипы.
|
||||
Цель анализа — определить, можно ли закрыть требования НИЯУ МИФИ готовым продуктом либо разработка собственной системы является обоснованной.
|
||||
|
||||
### 1.1. Российские системы
|
||||
|
||||
**Система «Учёт посещаемости студентов» ВВГУ.** Это внутренняя разработка Департамента цифрового развития Владивостокского государственного университета. По описанию ВВГУ, преподаватель во время занятия предоставляет студентам QR-код, при сканировании которого фиксируется присутствие; также возможна ручная отметка преподавателем. Система близка к целевому сценарию по способу фиксации, но является закрытым внутренним решением одного вуза. Для НИЯУ МИФИ она не закрывает требования интеграции с CAS, внешними академическими реестрами и собственной ролевой моделью. [Источник](https://www.vvsu.ru/news/201301/)
|
||||
|
||||
**БАРС.Образование / ЭЖД Мос.ру** Платформы ориентированы на электронный журнал и дневник школьного сегмента: оценки, темы уроков, расписание, домашние задания, посещаемость и отчётность. Ключевое ограничение состоит в самой модели предметной области: «школа — класс — родитель» плохо переносится на вузовскую структуру «поток — группа — подгруппа — кафедра — деканат». Посещаемость в таких системах, как правило, фиксируется учителем вручную; студенческая QR-самоотметка на очном занятии и интеграция с CAS вуза не являются базовым сценарием. [Источник](https://барс-образование.рф/about/)
|
||||
|
||||
**1С:Университет.** Решение на платформе «1С:Предприятие 8.3» предназначено для комплексной автоматизации процессов вуза: приёмной кампании, учебных планов, контингента, нагрузки преподавателей, успеваемости и посещаемости. Его сильная сторона — широкий охват административных процессов. Ограничение для данной работы — избыточность ERP-подхода и зависимость от экосистемы 1С. Для задачи точечной разработки веб-системы QR-самоотметки с CAS и REST/Outbox-синхронизацией такое решение не является оптимальным.
|
||||
|
||||
**iSpring LMS (ранее iSpring Learn).** Платформа ориентирована на корпоративное обучение: онлайн-курсы, тесты, тренинги, отчёты, API, SSO и on-premise-развёртывание. Функции посещаемости привязаны к тренингам и корпоративным мероприятиям, а не к вузовскому расписанию очных занятий. Система не предоставляет целевую модель «поток — группа — занятие — преподаватель — студент» и не решает задачу гарантированной синхронизации посещаемости с академическими реестрами НИЯУ МИФИ.
|
||||
|
||||
**Дневник.ру, ЭлЖур, Сетевой город. Образование.** Эти решения также относятся преимущественно к школьному электронному журналу. Их базовые сценарии строятся вокруг класса, учителя, родителей и ручной отметки посещаемости. Для корпоративного технического университета они не закрывают требования по CAS, QR-самоотметке, вузовской ролевой модели и on-premise-контролю данных.
|
||||
|
||||
### 1.2. Зарубежные системы
|
||||
|
||||
**Microsoft Teams.** В Teams поддерживаются отчёты о посещаемости онлайн-встреч: время входа, выхода, длительность участия и, в отдельных тарифах, показатели вовлечённости. Это полезно для дистанционных занятий, но не эквивалентно фиксации присутствия в аудитории. Кроме того, отчёты зависят от организатора встречи и не являются корпоративным реестром очной посещаемости. Для задачи НИЯУ МИФИ Teams не предоставляет QR-механизм, CAS-интеграцию и синхронизацию с внутренними академическими системами.
|
||||
|
||||
**Google Classroom / Google Meet.** Google Meet поддерживает отчёты о посещаемости для определённых редакций Google Workspace, включая Education Plus и Teaching and Learning Upgrade. Однако речь идёт о посещаемости онлайн-встреч, а не очных занятий. Google Classroom сам по себе не решает задачу аудиторной QR-самоотметки и не интегрируется с внутренними реестрами НИЯУ МИФИ в требуемой архитектуре.
|
||||
|
||||
**Brightspace (D2L).** Brightspace содержит инструмент Attendance, позволяющий создавать реестры посещаемости и отмечать статусы студентов. По своей природе базовый инструмент является электронным журналом внутри LMS. QR-самоотметка возможна через сторонние интеграции, например Qwickly Attendance, но это требует внедрения внешнего модуля и всей LMS-среды. Для НИЯУ МИФИ такой путь избыточен и не решает нативную интеграцию с CAS и внутренними реестрами.
|
||||
|
||||
### 1.3. Сравнительный анализ аналогов
|
||||
|
||||
Сравнительная оценка систем по критериям, важным для НИЯУ МИФИ, представлена в Таблице 1.
|
||||
|
||||
| Система | QR-self-service | SSO/CAS | Ролевая модель вуза | On-premise / контроль данных | Итог |
|
||||
| ------------------------ | :--------------: | :-------------------: | :-----------------: | :--------------------------: | ------------------------------------------------- |
|
||||
| ВВГУ «Учёт посещаемости» | + | - | Частично | + | Не переносится в МИФИ |
|
||||
| БАРС.Образование | - | - | Школьная | Зависит от внедрения | Не подходит в силу нацеленности на школьный фрмат |
|
||||
| 1С:Университет | - | - | + | + | Избыточная ERP-платформа |
|
||||
| iSpring LMS | - | SSO, не интегрируется | Корпоративная | + | Не подходит для вузовской QR-самоотметки |
|
||||
| Microsoft Teams | - | Azure AD | - | - | Только онлайн-встречи |
|
||||
| Google Meet/Classroom | - | Google | - | - | Только онлайн-встречи |
|
||||
| Brightspace (D2L) | Через интеграции | Зависит от внедрения | LMS | Зависит от контракта | Избыточно |
|
||||
| **Attendance MEPhI** | **+** | **(CAS МИФИ)** | **+** | **+** | **Целевое решение** |
|
||||
|
||||
*Таблица 1 — Сравнительная оценка систем учёта посещаемости*
|
||||
|
||||
Вывод по разделу: готового продукта, который одновременно обеспечивает QR-самоотметку очной посещаемости, интеграцию с CAS МИФИ, гарантированную синхронизацию с внутренними реестрами, полную вузовскую ролевую модель и контроль данных на стороне университета, не выявлено. Следовательно, разработка собственной системы является обоснованной.
|
||||
|
||||
## 2. Анализ методов фиксации посещаемости студентов
|
||||
|
||||
Для выбора основного механизма фиксации посещаемости необходимо сравнить возможные методы по инфраструктурным затратам, скорости, достоверности, правовым рискам и применимости в условиях многокорпусного университета.
|
||||
|
||||
**Ручная отметка преподавателем.** Традиционный метод не требует дополнительной инфраструктуры, но плохо масштабируется на крупные потоки. Он увеличивает нагрузку на преподавателя, создаёт задержку между фактом посещения и появлением данных в системе, а также оставляет риск ошибок при переносе данных.
|
||||
|
||||
**RFID/NFC-карты.** Метод позволяет быстро фиксировать предъявление студенческой карты или пропуска. Ограничения связаны с необходимостью оборудования аудиторий считывателями, стоимостью внедрения и риском передачи карты другому лицу. Кроме того, отметку нужно связать не просто с входом в корпус, а с конкретной дисциплиной, занятием и преподавателем.
|
||||
|
||||
**QR-коды.** Преподаватель генерирует QR-код для конкретного занятия, студент сканирует его смартфоном и проходит отметку через аутентифицированную сессию. Метод не требует установки считывателей в аудиториях, быстро внедряется и естественно связывается с расписанием, преподавателем и конкретной парой. Защита от злоупотреблений обеспечивается ограниченным временем жизни QR-кода, одноразовым токеном и проверкой личности через CAS.
|
||||
|
||||
**СКУД.** Системы контроля и управления доступов исторически являлись одним из самых надежных способов фиксации посещения, но использование их, как единственного метода — недостаточно, так как проход через турникет не гарантирует присутствие на лекции, но целесообразно использовать такие системы, как дополнительную проверку.
|
||||
|
||||
**Биометрическая идентификация.** Биометрия обеспечивает высокий уровень достоверности, но создаёт существенные правовые и организационные риски. Обработка биометрических персональных данных в РФ регулируется статьей 11 Федерального закона № 152-ФЗ «О персональных данных» и требует отдельного правового обоснования. Для учебного проекта и массового внедрения в аудиториях этот метод избыточен.
|
||||
|
||||
**Геолокация.** Геолокационная отметка не требует аудиторного оборудования, но точности GPS и Wi-Fi-позиционирования может быть недостаточно для различения смежных аудиторий. Кроме того, постоянная или регулярная обработка местоположения студентов повышает чувствительность решения с точки зрения приватности.
|
||||
|
||||
| Метод | Инфраструктура | Скорость | Защита от подмены | Правовые риски | Применимость в МИФИ |
|
||||
| -------------- | ------------------------------- | ------------------------- | ----------------------------- | -------------- | -------------------------------------- |
|
||||
| Ручная отметка | Минимальная | Низкая на больших потоках | Низкая | Минимальные | Резервный сценарий |
|
||||
| RFID/NFC | Высокие затраты на считыватели | Высокая | Средняя | Низкие | Ограниченная |
|
||||
| **QR-коды** | **Минимальная** | **Высокая** | **Средняя при токенах и CAS** | **Низкие** | **Оптимальная** |
|
||||
| СКУД | Затраты на оборудование | Высокая | Высокая | Низкие | Уже используется (возможна интеграция) |
|
||||
| Биометрия | Высокие затраты на оборудование | Высокая | Высокая | Высокие | Не рекомендуется |
|
||||
| Геолокация | Минимальная | Высокая | Низкая/средняя | Средние | Усложненная |
|
||||
|
||||
*Таблица 2 — Сравнительная оценка методов фиксации посещаемости*
|
||||
|
||||
Основным методом для системы целесообразно выбрать QR-коды. Ручная отметка преподавателем должна сохраниться как резервный механизм для спорных ситуаций, технических сбоев и студентов без доступа к смартфону в момент занятия.
|
||||
|
||||
---
|
||||
|
||||
## 3. Обзор существующих исследований
|
||||
|
||||
**QR-системы посещаемости.** В работе A. Nuhi, A. Memeti, F. Imeri и B. Çiço "Smart Attendance System using QR Code" предложена система учёта посещаемости на основе QR-кодов для высшего образования. Авторы указывают на типовые проблемы ручной регистрации: трудоёмкость, затраты времени и сложность работы с большими группами. Исследование подтверждает применимость QR-подхода для лекций и практических занятий, но описанная система функционирует автономно и не рассматривает интеграцию с корпоративным SSO, академическими реестрами и механизмами гарантированной доставки событий.
|
||||
|
||||
**Электронный журнал в структуре НИЯУ МИФИ.** Работа К.С. Глуховского, Р.В. Пирожкова и Е.А. Цвелика посвящена электронному журналу как элементу цифровой трансформации вуза. Для настоящей работы она важна тем, что рассматривает близкий контекст: электронную информационно-образовательную среду, импорт академических данных, разграничение ролей и отчётность. Ограничение такого подхода — отсутствие студенческой QR-самоотметки и описанного механизма надёжной синхронизации событий посещаемости.
|
||||
|
||||
**Практический опыт автоматизации очной посещаемости.** В публикации о методике автоматизации контроля посещаемости очных занятий по опыту кафедры информационных компьютерных технологий рассматривается переход от бумажной фиксации к цифровому учёту с использованием QR-инструментов и таблиц. Материал подтверждает практическую проблему крупных потоков и положительный эффект делегирования отметки студентам. При этом использование внешних облачных таблиц не подходит для целевой системы НИЯУ МИФИ из-за требований к контролю персональных данных и интеграции с внутренними системами.
|
||||
|
||||
**Производительность СУБД.** В статье S.V. Salunke и A. Ouda "A Performance Benchmark for the PostgreSQL and MySQL Databases" сравниваются PostgreSQL и MySQL на операциях INSERT, SELECT и UPDATE. В условиях эксперимента PostgreSQL показал преимущество на ряде транзакционных операций. Для "Attendance MEPhI" это важно, поскольку фиксация посещаемости создаёт пиковую нагрузку в начале занятия и требует атомарной записи основной отметки вместе с Outbox-событием. При этом вывод не следует трактовать как абсолютное превосходство PostgreSQL во всех сценариях: результаты бенчмарков зависят от профиля нагрузки, объёма данных, индексов и конфигурации.
|
||||
|
||||
**Архитектурный паттерн Transactional Outbox.** К. Ричардсон и AWS Prescriptive Guidance описывают паттерн Transactional Outbox, решающий проблему "dual write": несогласованности между записью в базу данных и отправкой события во внешнюю систему. Суть паттерна состоит в том, что событие записывается в outbox-таблицу в той же транзакции, что и бизнес-данные, а отдельный процесс-релей асинхронно публикует событие получателям. Паттерн обеспечивает доставку at-least-once, поэтому принимающая сторона должна быть идемпотентной.
|
||||
|
||||
Вывод по разделу: научные и отраслевые источники подтверждают применимость QR-кодов для учёта посещаемости, важность интеграции с вузовскими справочниками и необходимость архитектурного механизма надёжной синхронизации. Недостатки существующих подходов учитываются в проектируемой системе через CAS-аутентификацию, ролевую модель, PostgreSQL и Transactional Outbox.
|
||||
|
||||
---
|
||||
|
||||
## 4. Выбор технологического стека
|
||||
|
||||
Выбор технологического стека должен учитывать не только производительность отдельных компонентов, но и соответствие предметной области: быстрый CRUD, интеграция с SSO, работа с расписанием и группами, массовые отметки в начале занятий, генерация отчётов и надёжная доставка событий во внешние реестры.
|
||||
|
||||
### 4.1. Веб-фреймворк и архитектура приложения
|
||||
|
||||
Для реализации бэкенда выбран **Ruby on Rails 7**. Rails реализует архитектурный паттерн MVC, следует принципам Convention over Configuration и DRY, предоставляет зрелую ORM Active Record и ускоряет разработку типовых CRUD-компонентов: занятий, групп, студентов, преподавателей, ролей и отчётов.
|
||||
|
||||
Альтернативами могли быть Django, Laravel и Spring Boot. Django обладает зрелой экосистемой Python, но для server-side realtime-обновлений обычно требует дополнительных компонентов либо отдельного SPA-фронтенда. Laravel удобен для CRUD и имеет Livewire, но не соответствует текущему Ruby-стеку проекта. Spring Boot силён в корпоративной Java-разработке, но увеличивает объём шаблонного кода и замедляет итерации для относительно компактного веб-приложения.
|
||||
|
||||
Ограничение Rails — более низкая "сырая" вычислительная производительность Ruby по сравнению с Java и некоторыми другими рантаймами. В данной задаче это не является определяющим фактором: основная нагрузка связана с I/O, транзакциями БД, фоновыми задачами и отчётами, а не с тяжёлыми вычислениями в веб-процессе.
|
||||
|
||||
### 4.2. Фронтенд-архитектура: Hotwire
|
||||
|
||||
Для фронтенда выбран **Hotwire**: Turbo Drive, Turbo Frames, Turbo Streams и Stimulus. Hotwire использует подход HTML over the wire: сервер возвращает HTML-фрагменты, а не JSON для последующей сборки интерфейса на клиенте. Это позволяет получить отзывчивый интерфейс без отдельного React/Vue SPA.
|
||||
|
||||
Для системы учёта посещаемости такой подход подходит естественно. Основные действия пользователя — открыть список занятий, сгенерировать QR-код, отметить присутствие, обновить статус студента, посмотреть отчёт. Эти сценарии хорошо ложатся на Turbo Frames и Turbo Streams: можно точечно обновлять строки таблиц, счётчики и статусы без полной перезагрузки страницы.
|
||||
|
||||
Ограничение Hotwire проявилось бы в приложении со сложным клиентским состоянием, например в графическом редакторе или интерфейсе с большим количеством drag-and-drop логики. Для "Attendance MEPhI" таких требований нет, поэтому отказ от отдельного SPA снижает сложность разработки и поддержки.
|
||||
|
||||
### 4.3. СУБД: PostgreSQL
|
||||
|
||||
В качестве основной СУБД выбрана **PostgreSQL**. Для проекта важны ACID-транзакции, конкурентные записи, развитые индексы, строгая типизация, JSONB для метаданных событий и зрелые механизмы работы с ролями и схемами.
|
||||
|
||||
PostgreSQL особенно важен для реализации Transactional Outbox: отметка посещаемости и запись события синхронизации должны выполняться атомарно. Если запись отметки прошла успешно, но событие не было создано, внешние реестры могут остаться несогласованными. Если событие было отправлено без успешной записи отметки, возникнет обратная ошибка. Транзакционная модель PostgreSQL позволяет избежать этих сценариев.
|
||||
|
||||
### 4.4. Фоновые задачи: Sidekiq и Redis
|
||||
|
||||
Для фоновой обработки выбран **Sidekiq** с **Redis** в качестве хранилища очередей. Sidekiq использует многопоточную модель и хорошо интегрируется с Rails-приложениями.
|
||||
|
||||
В проектируемой системе Sidekiq решает три задачи:
|
||||
|
||||
1. Выполнение Outbox-релея: периодическая обработка необработанных событий посещаемости и отправка их во внешние реестры.
|
||||
2. Импорт справочников: загрузка расписания, групп, студентов и преподавателей из внутренних API без блокировки веб-интерфейса.
|
||||
3. Генерация отчётов: подготовка сводок для деканата и кураторов в фоне.
|
||||
|
||||
Redis используется как очередь фоновых задач и может дополнительно применяться для кэширования часто запрашиваемых справочников. При этом Redis не является источником истины для посещаемости: все критичные данные должны храниться в PostgreSQL.
|
||||
|
||||
### 4.5. Аутентификация: CAS
|
||||
|
||||
Для аутентификации используется **CAS (Central Authentication Service)**. CAS — билетный протокол единого входа: пользователь вводит логин и пароль на доверенном CAS-сервере, после чего приложение получает service ticket и валидирует его. Веб-приложение не хранит и не обрабатывает пароль пользователя.
|
||||
|
||||
Для НИЯУ МИФИ это принципиально важно. Система посещаемости должна использовать существующие учётные записи студентов и преподавателей, не создавая отдельную базу паролей. CAS отвечает за аутентификацию, а авторизация остаётся задачей приложения: после входа система должна определить роль пользователя и доступные интерфейсы.
|
||||
|
||||
### 4.6. Архитектурные паттерны
|
||||
|
||||
**Service Objects.** Сложная бизнес-логика выносится из контроллеров и моделей в отдельные сервисные объекты. Примеры операций: импорт студентов, импорт расписания, генерация QR-кода, фиксация посещения, создание Outbox-события. Такой подход упрощает тестирование и снижает связанность кода.
|
||||
|
||||
**Namespaced Controllers.** Контроллеры разделяются по пространствам имён: `Admin::`, `Teacher::`, `Student::`, `Moderator::` или аналогичным. Это отражает ролевую модель в структуре приложения, упрощает маршрутизацию и снижает риск случайного смешения интерфейсов.
|
||||
|
||||
**Transactional Outbox.** При фиксации посещения приложение записывает основную отметку и событие синхронизации в одной транзакции. Затем фоновый воркер отправляет событие во внешний реестр. Если отправка не удалась, событие остаётся в outbox-таблице и будет обработано повторно. Получатель должен обеспечивать идемпотентность, например по уникальному идентификатору события.
|
||||
|
||||
### 4.7. Стилизация и деплой
|
||||
|
||||
Для стилизации выбран **Tailwind CSS**. Utility-first подход удобен для быстрого создания интерфейсов с небольшим production-бандлом и без жёсткой зависимости от визуального стиля готового UI-фреймворка. **Bootstrap 5** может использоваться точечно для готовых компонентов, если это не усложняет сборку и не создаёт конфликтов стилей.
|
||||
|
||||
Деплой целесообразно автоматизировать через **Capistrano**, так как это стандартный инструмент для Ruby/Rails-приложений с поддержкой релизов, rollback и разделения окружений. **Mise** может использоваться для фиксации версий Ruby, Node.js и сопутствующих рантаймов между локальной разработкой, CI и сервером.
|
||||
|
||||
Итоговый технологический стек:
|
||||
|
||||
| Компонент | Выбор | Обоснование |
|
||||
|---|---|---|
|
||||
| Backend | Ruby on Rails 7 | Быстрая разработка CRUD, MVC, зрелая экосистема |
|
||||
| Frontend | Hotwire (Turbo + Stimulus) | Интерактивность без отдельного SPA |
|
||||
| Database | PostgreSQL | ACID, JSONB, транзакции для Outbox |
|
||||
| Background jobs | Sidekiq + Redis | Фоновые импорты, отчёты, Outbox-релей |
|
||||
| Auth | CAS | SSO, приложение не хранит пароли |
|
||||
| UI | Tailwind CSS, точечно Bootstrap | Быстрая стилизация и готовые компоненты |
|
||||
| Deploy | Capistrano + Mise | Повторяемый деплой и контроль версий рантаймов |
|
||||
|
||||
---
|
||||
|
||||
## Заключение
|
||||
|
||||
В ходе учебно-исследовательской работы проведён анализ существующих систем учёта посещаемости, методов фиксации присутствия студентов и технологического стека для разработки системы "Attendance MEPhI".
|
||||
|
||||
Установлено, что готовые российские и зарубежные продукты не закрывают совокупность требований НИЯУ МИФИ. Школьные электронные журналы ориентированы на модель класса и ручную отметку. ERP-решения уровня 1С:Университет избыточны для задачи QR-самоотметки и завязаны на собственную экосистему. LMS и сервисы видеоконференций фиксируют либо активность внутри курса, либо участие в онлайн-встрече, но не очную посещаемость в аудитории. QR-SaaS-сервисы создают риски внешнего хранения персональных данных и vendor lock-in. Открытые прототипы не обладают промышленной надёжностью и корпоративной интеграцией.
|
||||
|
||||
Из рассмотренных методов фиксации посещаемости оптимальным признан QR-код с ограниченным временем жизни, одноразовым токеном и проверкой пользователя через CAS. Ручная отметка преподавателем должна оставаться резервным механизмом. RFID/NFC требует инфраструктурных затрат, биометрия создаёт высокие правовые риски, а геолокация недостаточно точна для аудиторного сценария и чувствительна с точки зрения приватности.
|
||||
|
||||
Выбранный стек — Ruby on Rails 7, Hotwire, PostgreSQL, Sidekiq, Redis, CAS, Tailwind CSS, Capistrano и Mise — соответствует задаче разработки корпоративного веб-приложения. Rails и Hotwire ускоряют создание интерфейсов без отдельного SPA, PostgreSQL обеспечивает транзакционную целостность, Sidekiq реализует фоновые процессы, а Transactional Outbox снижает риск потери событий при синхронизации с внешними реестрами.
|
||||
|
||||
Результаты работы формируют обоснованную теоретическую и техническую базу для реализации системы "Attendance MEPhI" в рамках последующей проектной или дипломной работы.
|
||||
|
||||
---
|
||||
|
||||
## Список использованной литературы
|
||||
|
||||
1. Nuhi A., Memeti A., Imeri F., Çiço B. Smart Attendance System using QR Code // 2020 9th Mediterranean Conference on Embedded Computing (MECO), Budva, Montenegro, 2020. P. 1-4. DOI: 10.1109/MECO49872.2020.9134225.
|
||||
2. Глуховский К.С., Пирожков Р.В., Цвелик Е.А. Электронный журнал как элемент цифровой трансформации вуза // Инженерный вестник Дона. 2021. № 5. URL: https://ivdon.ru/ru/magazine/archive/n5y2021/6978 (дата обращения: 08.06.2026).
|
||||
3. Дусалин А.К., Семенов Г.Н., Скичко Е.А. Методика автоматизации контроля посещаемости очных занятий по опыту кафедры информационных компьютерных технологий. URL: https://www.muctr.ru/upload/iblock/409/hd3gn9gkbrnezs3kuedj3lz18cb2oddx.pdf (дата обращения: 08.06.2026).
|
||||
4. Richardson C. Microservices Patterns: With Examples in Java. Manning Publications, 2018. 520 p.
|
||||
5. Transactional Outbox Pattern // AWS Prescriptive Guidance. URL: https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/transactional-outbox.html (дата обращения: 08.06.2026).
|
||||
6. Salunke S.V., Ouda A. A Performance Benchmark for the PostgreSQL and MySQL Databases // Future Internet. 2024. Vol. 16, No. 10. Article 382. DOI: 10.3390/fi16100382.
|
||||
7. Getting Started with Rails // Ruby on Rails Guides. URL: https://guides.rubyonrails.org/getting_started.html (дата обращения: 08.06.2026).
|
||||
8. Hotwire: HTML Over The Wire // Hotwire. URL: https://hotwired.dev/ (дата обращения: 08.06.2026).
|
||||
9. Sidekiq: Simple, Efficient Background Jobs for Ruby // GitHub. URL: https://github.com/sidekiq/sidekiq (дата обращения: 08.06.2026).
|
||||
10. PostgreSQL Documentation // PostgreSQL. URL: https://www.postgresql.org/docs/ (дата обращения: 08.06.2026).
|
||||
11. CAS Protocol // Apereo CAS. URL: https://apereo.github.io/cas/7.1.x/protocol/CAS-Protocol.html (дата обращения: 08.06.2026).
|
||||
12. Tailwind CSS Documentation // Tailwind CSS. URL: https://tailwindcss.com/docs (дата обращения: 08.06.2026).
|
||||
13. Bootstrap 5 Documentation // Bootstrap. URL: https://getbootstrap.com/docs/5.3/ (дата обращения: 08.06.2026).
|
||||
14. Capistrano: Remote Server Automation and Deployment Tool // Capistrano. URL: https://capistranorb.com/ (дата обращения: 08.06.2026).
|
||||
15. Mise: Polyglot Runtime Manager // Mise. URL: https://mise.jdx.dev/ (дата обращения: 08.06.2026).
|
||||
16. Система "Учёт посещаемости студентов" // Электронный кампус ВВГУ. URL: https://www.vvsu.ru/e-campus/news/201301/ (дата обращения: 08.06.2026).
|
||||
17. БАРС.Образование — Электронная Школа: описание программного обеспечения // БАРС Груп. URL: https://bars.group/wp-content/uploads/2024/05/opisanie-elektronnaya-shkola.pdf (дата обращения: 08.06.2026).
|
||||
18. 1С:Университет — Возможности // 1С. URL: https://solutions.1c.ru/catalog/university/features (дата обращения: 08.06.2026).
|
||||
19. iSpring LMS (iSpring Learn): платформа для онлайн-обучения // iSpring. URL: https://www.ispring.ru/ispring-learn (дата обращения: 08.06.2026).
|
||||
20. On-Premise LMS for Effective & Secure Employee Training // iSpring Solutions. URL: https://www.ispringsolutions.com/ispring-learn/on-premise-lms (дата обращения: 08.06.2026).
|
||||
21. Manage meeting attendance reports in Microsoft Teams // Microsoft Support. URL: https://support.microsoft.com/en-US/teams/meetings/manage-meeting-attendance-reports-in-microsoft-teams (дата обращения: 08.06.2026).
|
||||
22. Track attendance & view Live stream report // Google Meet Help. URL: https://support.google.com/meet/answer/10090454?hl=en (дата обращения: 08.06.2026).
|
||||
23. About Attendance // Brightspace Community. URL: https://community.d2l.com/brightspace/kb/articles/3609-about-attendance (дата обращения: 08.06.2026).
|
||||
24. Qwickly Attendance Pro // D2L Brightspace IntegrationHub. URL: https://integrationhub.brightspace.com/details/qwickly-attendance-pro (дата обращения: 08.06.2026).
|
||||
25. Федеральный закон от 27.07.2006 № 152-ФЗ "О персональных данных", статья 11 "Биометрические персональные данные" // КонсультантПлюс. URL: https://www.consultant.ru/document/cons_doc_LAW_61801/7336c78762a98b5f4f698b8c3800dca1111acc16/ (дата обращения: 08.06.2026).
|
||||
@@ -0,0 +1,224 @@
|
||||
---
|
||||
title: CI/CD — основы
|
||||
status: processing
|
||||
type: concept
|
||||
tags:
|
||||
- devops
|
||||
- cicd
|
||||
- gitlab-ci
|
||||
- jenkins
|
||||
created: 2026-06-12
|
||||
updated: 2026-06-16
|
||||
aliases:
|
||||
- Основы CI/CD
|
||||
- Непрерывная интеграция и доставка
|
||||
source:
|
||||
- "CIСD Конвейер для сборки и доставки продукта. Знакомство с GitlabCI и Jenkins"
|
||||
- "[[CI-CD Конвейер для сборки и доставки продукта. Построение пайплайнов]]"
|
||||
---
|
||||
|
||||
# CI/CD — Основы
|
||||
|
||||
**CI/CD** — набор практик автоматизации интеграции, проверки, сборки и доставки изменений программного продукта. Он сокращает цикл обратной связи и делает выпуск изменений повторяемым.
|
||||
|
||||
CI/CD связывает управление кодом через [[Git]], командную работу в платформах вроде [[Gitlab]] и развёртывание продукта в целевой инфраструктуре. Это одна из базовых практик [[DevOps - основы]].
|
||||
|
||||
## Состав CI/CD
|
||||
|
||||
### Continuous Integration
|
||||
|
||||
**Continuous Integration (CI)** — частое объединение небольших изменений в общую кодовую базу с автоматической проверкой каждого изменения.
|
||||
|
||||
Типичный CI-процесс:
|
||||
|
||||
1. Разработчик отправляет изменения в репозиторий.
|
||||
2. Система запускает статический анализ и автоматические тесты.
|
||||
3. Приложение собирается или компилируется.
|
||||
4. Результат упаковывается в версионированный артефакт.
|
||||
5. Артефакт сохраняется в реестре или хранилище.
|
||||
|
||||
Unit-тесты запускают отдельные единицы кода в контролируемом окружении. Кроме них в CI могут входить линтеры, SAST, SCA, интеграционные тесты и проверки качества.
|
||||
|
||||
### Continuous Delivery
|
||||
|
||||
**Continuous Delivery** — поддержание проверенного артефакта в состоянии готовности к выпуску. Доставка до production обычно требует явного решения или ручного подтверждения.
|
||||
|
||||
![[Pasted image 20260612125256.png|353]]
|
||||
|
||||
### Continuous Deployment
|
||||
|
||||
**Continuous Deployment** — автоматическое развёртывание каждого изменения, прошедшего все проверки, в production без ручного подтверждения.
|
||||
|
||||
Delivery и deployment часто обозначают одной аббревиатурой CD, но уровень автоматизации выпуска у них различается.
|
||||
|
||||
![[Pasted image 20260612125409.png]]
|
||||
|
||||
## Пайплайн
|
||||
|
||||
**Pipeline** — описанная последовательность стадий и задач CI/CD. Обычно новый запуск инициируется коммитом, merge request, тегом, расписанием или ручным действием.
|
||||
|
||||
Распространённые стадии:
|
||||
|
||||
1. получение исходного кода;
|
||||
2. линтинг и статический анализ;
|
||||
3. модульное и интеграционное тестирование;
|
||||
4. сборка приложения;
|
||||
5. упаковка артефакта, например контейнерного образа на основе [[Основы - Docker]];
|
||||
6. публикация артефакта;
|
||||
7. развёртывание;
|
||||
8. дополнительные проверки;
|
||||
9. сбор метрик и обратной связи.
|
||||
|
||||
Отдельная задача внутри pipeline обычно называется **job**. Jobs объединяются в stages и выполняются на выделенных исполнителях.
|
||||
|
||||
Stages задают крупные фазы процесса, например `build`, `test`, `deploy`. Jobs внутри разных stages обычно выполняются последовательно по порядку стадий, а несколько jobs внутри одной stage могут выполняться параллельно, если есть свободные исполнители.
|
||||
|
||||
Минимальный пример `.gitlab-ci.yml`:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
- deploy
|
||||
|
||||
build-job:
|
||||
stage: build
|
||||
script:
|
||||
- make build
|
||||
|
||||
test-job:
|
||||
stage: test
|
||||
script:
|
||||
- make test
|
||||
|
||||
deploy-job:
|
||||
stage: deploy
|
||||
script:
|
||||
- make deploy
|
||||
```
|
||||
|
||||
## Артефакт
|
||||
|
||||
**Артефакт** — неизменяемый результат сборки, который можно проверить, хранить и продвигать между средами. Это может быть бинарный файл, пакет, архив или контейнерный образ.
|
||||
|
||||
Один и тот же проверенный артефакт желательно последовательно использовать во всех средах. Повторная сборка перед production создаёт риск получить результат, отличный от протестированного.
|
||||
|
||||
## Среды
|
||||
|
||||
Продукт проходит через несколько изолированных сред:
|
||||
|
||||
- **Dev** — разработка и ранняя интеграция;
|
||||
- **Test / QA / Integration** — функциональные и интеграционные проверки;
|
||||
- **Stage / Pre-production / UAT** — проверка в окружении, близком к production;
|
||||
- **Prod** — эксплуатация продукта пользователями.
|
||||
|
||||
Развёртывание может выполняться на виртуальных машинах, в контейнерах или в оркестраторе, например [[Kubernetes - MOC]].
|
||||
|
||||
## Обратная связь
|
||||
|
||||
Pipeline не заканчивается фактом развёртывания. После выпуска необходимо собирать:
|
||||
|
||||
- метрики приложения и инфраструктуры;
|
||||
- логи;
|
||||
- трассировки;
|
||||
- сведения об ошибках;
|
||||
- продуктовые и пользовательские метрики.
|
||||
|
||||
Эти данные возвращаются в цикл разработки и помогают оценить качество релиза. Наблюдаемость pipeline также показывает длительность задач, частоту сбоев и узкие места доставки.
|
||||
|
||||
## GitLab CI/CD
|
||||
|
||||
**GitLab CI/CD** — встроенная в [[Gitlab]] система автоматизации pipeline. Конфигурация обычно хранится рядом с кодом в файле `.gitlab-ci.yml`.
|
||||
|
||||
Jobs выполняются компонентами **GitLab Runner**. Runner может работать на физическом сервере, виртуальной машине, в Docker или Kubernetes.
|
||||
|
||||
Конфигурация GitLab CI/CD сочетает декларативный YAML и команды shell. YAML описывает stages, jobs, переменные, зависимости и условия запуска. Shell-команды внутри `script` выполняют конкретные действия: сборку, тесты, публикацию артефактов или деплой.
|
||||
|
||||
Pipeline можно запускать:
|
||||
|
||||
- вручную через веб-интерфейс или API;
|
||||
- автоматически по push, commit, merge request, tag и другим событиям;
|
||||
- через webhook или интеграцию с внешней системой;
|
||||
- с параметрами: переменными, выбранной веткой, тегом, коммитом или окружением.
|
||||
|
||||
В сложных проектах pipeline могут быть составными: родительский pipeline запускает дочерние pipeline, а отдельные части процесса выполняются последовательно или параллельно. Это помогает разделять сборку, тестирование, деплой и проверки безопасности по независимым конфигурациям.
|
||||
|
||||
Преимущества:
|
||||
|
||||
- тесная интеграция с репозиториями, merge request и правами GitLab;
|
||||
- pipeline as code;
|
||||
- управление jobs, stages, артефактами и окружениями в одном интерфейсе;
|
||||
- просмотр состояния и результатов задач;
|
||||
- возможность масштабирования runners.
|
||||
|
||||
Ограничения:
|
||||
|
||||
- наиболее удобен внутри экосистемы GitLab;
|
||||
- сложные pipeline требуют аккуратной организации YAML-конфигурации;
|
||||
- поведение артефактов, cache и зависимостей между jobs необходимо явно проектировать.
|
||||
|
||||
## Jenkins
|
||||
|
||||
**Jenkins** — самостоятельный open-source сервер автоматизации. Pipeline можно описывать декларативно или программно в `Jenkinsfile` с использованием Groovy.
|
||||
|
||||
Для Jenkins важна экосистема плагинов: через них подключаются системы контроля версий, учётные данные, агенты, уведомления, Kubernetes, инструменты безопасности и observability. Плагины упрощают интеграции, но требуют регулярного обновления и контроля совместимости.
|
||||
|
||||
Pipeline можно настроить через веб-интерфейс, но для воспроизводимости лучше хранить `Jenkinsfile` в репозитории рядом с кодом. Тогда изменения pipeline проходят тот же контроль, что и изменения продукта: review, merge request, история коммитов и ограничения доступа.
|
||||
|
||||
Запуск Jenkins pipeline возможен:
|
||||
|
||||
- вручную через веб-интерфейс или API;
|
||||
- автоматически через интеграции и плагины;
|
||||
- через webhook из GitLab или другой VCS-платформы;
|
||||
- по расписанию.
|
||||
|
||||
При описании pipeline на Groovy Jenkins может генерировать фрагменты синтаксиса через встроенный помощник. Это снижает риск ошибки при работе с параметрами шагов и плагинов.
|
||||
|
||||
Преимущества:
|
||||
|
||||
- большая экосистема плагинов;
|
||||
- интеграция со множеством систем контроля версий, сборки и развёртывания;
|
||||
- гибкость при построении нестандартных процессов;
|
||||
- развитое сообщество и документация.
|
||||
|
||||
Ограничения:
|
||||
|
||||
- сервер и агенты необходимо отдельно устанавливать, обновлять и защищать;
|
||||
- плагины увеличивают поверхность атаки и стоимость сопровождения;
|
||||
- конфликты версий и накопление плагинов усложняют обновления;
|
||||
- для небольших команд эксплуатация может быть тяжелее встроенного решения.
|
||||
|
||||
## Развитие CI/CD
|
||||
|
||||
CI/CD внедряют поэтапно: сначала автоматизируют сборку и базовые проверки, затем добавляют доставку артефактов, деплой, наблюдаемость и проверки безопасности. Процесс не должен останавливаться после первого рабочего pipeline.
|
||||
|
||||
Практики, которые усиливают CI/CD:
|
||||
|
||||
- [[IaC - основы]] — декларативное описание инфраструктуры и конфигурации;
|
||||
- контейнеризация через [[Основы - Docker]] и оркестрация через [[Kubernetes - MOC]];
|
||||
- observability: метрики, логи, трассировки, аудит, обработка логов и обратная связь;
|
||||
- DevSecOps-проверки: SAST, SCA, DAST, IAST, RASP, сканирование инфраструктуры и работа с секретами.
|
||||
|
||||
Эти проверки можно добавлять как отдельные stages или jobs. Главное - не превращать pipeline в непрозрачный набор ручных действий: сборка, тесты, доставка и контроль качества должны быть описаны как код и воспроизводиться одинаково.
|
||||
|
||||
## GitLab CI/CD и Jenkins
|
||||
|
||||
| Критерий | GitLab CI/CD | Jenkins |
|
||||
|---|---|---|
|
||||
| Модель | Встроен в GitLab | Самостоятельный сервер |
|
||||
| Описание pipeline | YAML | Jenkinsfile, Groovy |
|
||||
| Исполнители | GitLab Runner | Jenkins agents |
|
||||
| Интеграции | Нативные возможности GitLab | Обширная система плагинов |
|
||||
| Сопровождение | Зависит от способа размещения GitLab и runners | Требует сопровождения controller, agents и плагинов |
|
||||
| Основной сценарий | Проекты, уже использующие GitLab | Гетерогенная инфраструктура и нестандартные интеграции |
|
||||
|
||||
Если код и процессы команды находятся в GitLab, GitLab CI/CD обычно требует меньше интеграционной работы. Jenkins оправдан, когда важны независимость от одной платформы, существующая Jenkins-инфраструктура или специфические интеграции.
|
||||
|
||||
## Связанные заметки
|
||||
|
||||
- [[DevOps - основы]]
|
||||
- [[Git]]
|
||||
- [[Gitlab]]
|
||||
- [[IaC - основы]]
|
||||
- [[Основы - Docker]]
|
||||
- [[Kubernetes - MOC]]
|
||||
@@ -6,11 +6,12 @@ tags:
|
||||
- infrastructure
|
||||
- devops
|
||||
created: 2026-05-18
|
||||
updated: 2026-05-18
|
||||
updated: 2026-06-15
|
||||
source: '[[Infrastructure]]'
|
||||
aliases:
|
||||
- Infrastructure — ada-dev
|
||||
---
|
||||
|
||||
# Infrastructure — ada-dev
|
||||
|
||||
Заметка фиксирует текущую схему homelab-инфраструктуры `ada-dev`: публичный VPS, домашние узлы, reverse proxy, overlay-сеть и пользовательские сервисы.
|
||||
@@ -29,7 +30,7 @@ VPS принимает внешний трафик и проксирует ег
|
||||
|
||||
## Узлы
|
||||
|
||||
### ru-vps
|
||||
### Ru-vps
|
||||
|
||||
`ru-vps` — публичный сервер с белым IP. На схеме он выполняет роль внешнего шлюза:
|
||||
|
||||
@@ -41,7 +42,7 @@ VPS принимает внешний трафик и проксирует ег
|
||||
|
||||
Практический смысл VPS — держать наружу только минимальный набор портов и не раскрывать домашнюю сеть напрямую.
|
||||
|
||||
### mini-pc
|
||||
### Mini-pc
|
||||
|
||||
`mini-pc` описан как `N150 / 12 GB / 256 GB`. По схеме это небольшой домашний узел для инфраструктурных сервисов:
|
||||
|
||||
@@ -53,7 +54,7 @@ VPS принимает внешний трафик и проксирует ег
|
||||
|
||||
Логика узла: локальная инфраструктура и сервисы, которым полезно быть рядом с домашней сетью.
|
||||
|
||||
### cloud-pc
|
||||
### Cloud-pc
|
||||
|
||||
`cloud-pc` описан как `i5 / 16 GB / 256 GB + 5 TB`. Это более крупный узел для сервисов и хранения:
|
||||
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
---
|
||||
title: Proxmox VE — local, local-lvm, LVM
|
||||
status: seed
|
||||
type: guide
|
||||
tags:
|
||||
- homelab
|
||||
- proxmox
|
||||
- lvm
|
||||
- storage
|
||||
created: 2025-12-17
|
||||
updated: 2026-06-15
|
||||
aliases: []
|
||||
source: "[[Proxmox VE — local, local-lvm, LVM — source]]"
|
||||
---
|
||||
|
||||
# Proxmox VE — local, local-lvm, LVM
|
||||
|
||||
## Что такое local и local-lvm
|
||||
|
||||
|Storage|Тип|Путь / устройство|Хранит|
|
||||
|---|---|---|---|
|
||||
|`local`|directory|`/var/lib/vz`|ISO, CT-шаблоны, бэкапы, snippets|
|
||||
|`local-lvm`|LVM thin pool|`pve/data` (LV в VG `pve`)|Диски VM/CT, снапшоты дисков|
|
||||
|
||||
## Куда скачиваются ISO через веб-интерфейс
|
||||
|
||||
По умолчанию — в `local` (`/var/lib/vz/template/iso/`).
|
||||
Чтобы скачивать на другой storage — выбрать его в выпадающем списке при Download from URL.
|
||||
Условие: у storage должен быть включён Content type **ISO image** (Datacenter → Storage → Edit → Content).
|
||||
|
||||
## Проверить содержимое LVM
|
||||
|
||||
```bash
|
||||
# Список всех LV с размерами
|
||||
lvs
|
||||
|
||||
# Storage Proxmox с backing-устройствами
|
||||
pvesm status
|
||||
```
|
||||
|
||||
Thin pool определяется по атрибуту `twi` в колонке `Attr`.
|
||||
|
||||
## Удалить local-lvm и расширить root
|
||||
|
||||
> Перед удалением убедиться, что на `local-lvm` нет дисков VM/CT (Data% = 0.00).
|
||||
|
||||
### 1. Удалить thin pool
|
||||
|
||||
```bash
|
||||
sudo lvremove /dev/pve/data
|
||||
# подтвердить: y
|
||||
```
|
||||
|
||||
### 2. Расширить root на нужный размер
|
||||
|
||||
```bash
|
||||
# Добавить конкретный размер (например, 60G):
|
||||
sudo lvresize -L +60G /dev/pve/root
|
||||
|
||||
# Или отдать всё свободное место:
|
||||
sudo lvresize -l +100%FREE /dev/pve/root
|
||||
```
|
||||
|
||||
### 3. Расширить файловую систему
|
||||
|
||||
```bash
|
||||
sudo resize2fs /dev/pve/root
|
||||
```
|
||||
|
||||
Работает online, перезагрузка не нужна.
|
||||
|
||||
### 4. Удалить storage из GUI
|
||||
|
||||
Datacenter → Storage → `local-lvm` → Remove
|
||||
|
||||
### 5. Проверить результат
|
||||
|
||||
```bash
|
||||
lsblk
|
||||
lvs
|
||||
df -h /
|
||||
```
|
||||
|
||||
## Что делать с оставшимся свободным местом в VG pve
|
||||
|
||||
Нераспределённое пространство можно использовать:
|
||||
|
||||
- добавить как новый LVM storage в Proxmox
|
||||
- создать новый LV под конкретную задачу
|
||||
- оставить в резерве
|
||||
|
||||
```bash
|
||||
# Посмотреть свободное место в VG:
|
||||
vgs
|
||||
```
|
||||
|
||||
## Связанные заметки
|
||||
|
||||
- [[Infrastructure — ada-dev]]
|
||||
@@ -0,0 +1,403 @@
|
||||
---
|
||||
title: Sudoers
|
||||
status: seed
|
||||
type: guide
|
||||
tags:
|
||||
- linux
|
||||
- sudo
|
||||
- security
|
||||
created: 2026-06-15
|
||||
updated: 2026-06-15
|
||||
aliases:
|
||||
- sudoers
|
||||
- Настройка sudo
|
||||
source: "[[Sudoers — source]]"
|
||||
---
|
||||
|
||||
# Sudoers
|
||||
|
||||
`sudo` запускает команду с полномочиями другого пользователя, обычно `root`. Политика доступа задаётся в `/etc/sudoers` и дополнительных файлах из `/etc/sudoers.d/`.
|
||||
|
||||
`sudo` не является заменой файловых прав. Он выдаёт полномочия на конкретное действие после проверки пользователя, хоста, целевого пользователя, команды и параметров политики.
|
||||
|
||||
## Где хранится конфигурация
|
||||
|
||||
- `/etc/sudoers` — основной файл.
|
||||
- `/etc/sudoers.d/` — каталог для отдельных правил.
|
||||
- `/var/log/` или журнал systemd — записи о вызовах `sudo`, в зависимости от конфигурации системы.
|
||||
|
||||
Основной файл обычно содержит:
|
||||
|
||||
```sudoers
|
||||
@includedir /etc/sudoers.d
|
||||
```
|
||||
|
||||
Для локальных правил удобнее создавать отдельный файл:
|
||||
|
||||
```bash
|
||||
sudo visudo -f /etc/sudoers.d/backup
|
||||
```
|
||||
|
||||
Имена файлов в `sudoers.d` лучше делать без точки и символа `~`: некоторые реализации игнорируют такие файлы. Права должны исключать запись обычными пользователями:
|
||||
|
||||
```bash
|
||||
sudo chown root:root /etc/sudoers.d/backup
|
||||
sudo chmod 0440 /etc/sudoers.d/backup
|
||||
```
|
||||
|
||||
## Редактирование через visudo
|
||||
|
||||
Не следует редактировать `/etc/sudoers` обычным редактором. `visudo` блокирует файл от одновременного изменения и проверяет синтаксис перед сохранением.
|
||||
|
||||
```bash
|
||||
sudo visudo
|
||||
sudo visudo -f /etc/sudoers.d/backup
|
||||
sudo visudo -c
|
||||
```
|
||||
|
||||
Если ошибка уже внесена, исправление потребует действующей root-сессии, загрузки в recovery mode или другого способа получить root-доступ.
|
||||
|
||||
Редактор можно выбрать переменными окружения:
|
||||
|
||||
```bash
|
||||
sudo EDITOR=nvim visudo
|
||||
```
|
||||
|
||||
Возможность выбора редактора зависит от настроек `sudo`; небезопасные переменные окружения могут быть отброшены.
|
||||
|
||||
## Формат правила
|
||||
|
||||
Базовая форма:
|
||||
|
||||
```sudoers
|
||||
пользователь хост=(целевой_пользователь:целевая_группа) теги: команды
|
||||
```
|
||||
|
||||
Пример:
|
||||
|
||||
```sudoers
|
||||
ada ALL=(root) /usr/bin/systemctl restart nginx
|
||||
```
|
||||
|
||||
Значение полей:
|
||||
|
||||
| Поле | Смысл |
|
||||
|---|---|
|
||||
| `ada` | кому разрешено использовать правило |
|
||||
| `ALL` | на каких хостах действует правило |
|
||||
| `(root)` | от имени какого пользователя разрешён запуск |
|
||||
| `/usr/bin/systemctl restart nginx` | разрешённая команда и аргументы |
|
||||
|
||||
Группа указывается с `%`:
|
||||
|
||||
```sudoers
|
||||
%wheel ALL=(ALL:ALL) ALL
|
||||
```
|
||||
|
||||
Это разрешает членам группы `wheel` запускать любые команды от имени любого пользователя и группы. Такое правило выдаёт полный административный доступ.
|
||||
|
||||
## Команды и аргументы
|
||||
|
||||
В правилах следует использовать абсолютный путь:
|
||||
|
||||
```sudoers
|
||||
ada ALL=(root) /usr/bin/systemctl status nginx
|
||||
```
|
||||
|
||||
Путь можно узнать командой:
|
||||
|
||||
```bash
|
||||
command -v systemctl
|
||||
```
|
||||
|
||||
Аргументы являются частью разрешения. Эти правила различаются:
|
||||
|
||||
```sudoers
|
||||
ada ALL=(root) /usr/bin/systemctl restart nginx
|
||||
ada ALL=(root) /usr/bin/systemctl restart *
|
||||
```
|
||||
|
||||
Первое разрешает перезапустить только `nginx`. Второе намного шире и может разрешить перезапуск произвольного юнита.
|
||||
|
||||
Правило без аргументов в современных версиях `sudo` может разрешать команду с любыми аргументами. Для явного запрета аргументов используется пустая строка:
|
||||
|
||||
```sudoers
|
||||
ada ALL=(root) /usr/bin/id ""
|
||||
```
|
||||
|
||||
Поведение сопоставления аргументов и специальных символов зависит от версии `sudo`. После изменения правило нужно проверять на целевой системе.
|
||||
|
||||
## Псевдонимы
|
||||
|
||||
Псевдонимы уменьшают повторение в больших конфигурациях.
|
||||
|
||||
### User_Alias
|
||||
|
||||
```sudoers
|
||||
User_Alias OPERATORS = ada, alice, %ops
|
||||
```
|
||||
|
||||
### Runas_Alias
|
||||
|
||||
```sudoers
|
||||
Runas_Alias SERVICE_USERS = nginx, postgres
|
||||
```
|
||||
|
||||
### Host_Alias
|
||||
|
||||
```sudoers
|
||||
Host_Alias WEB_SERVERS = web01, web02
|
||||
```
|
||||
|
||||
### Cmnd_Alias
|
||||
|
||||
```sudoers
|
||||
Cmnd_Alias NGINX_CONTROL = \
|
||||
/usr/bin/systemctl status nginx, \
|
||||
/usr/bin/systemctl reload nginx, \
|
||||
/usr/bin/systemctl restart nginx
|
||||
|
||||
%ops WEB_SERVERS=(root) NGINX_CONTROL
|
||||
```
|
||||
|
||||
Имена псевдонимов принято писать в верхнем регистре.
|
||||
|
||||
## Теги
|
||||
|
||||
### PASSWD и NOPASSWD
|
||||
|
||||
По умолчанию `sudo` запрашивает пароль вызывающего пользователя:
|
||||
|
||||
```sudoers
|
||||
ada ALL=(root) /usr/bin/systemctl restart nginx
|
||||
```
|
||||
|
||||
Без пароля:
|
||||
|
||||
```sudoers
|
||||
ada ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx
|
||||
```
|
||||
|
||||
`NOPASSWD` удобно для автоматизации, но увеличивает последствия компрометации учётной записи. Его следует ограничивать минимальным набором команд.
|
||||
|
||||
Теги действуют на следующие команды в списке, пока не будут переопределены:
|
||||
|
||||
```sudoers
|
||||
ada ALL=(root) NOPASSWD: /usr/bin/systemctl status nginx, \
|
||||
PASSWD: /usr/bin/systemctl restart nginx
|
||||
```
|
||||
|
||||
### NOEXEC
|
||||
|
||||
```sudoers
|
||||
ada ALL=(root) NOEXEC: /usr/bin/less /var/log/nginx/error.log
|
||||
```
|
||||
|
||||
`NOEXEC` пытается запретить программе запускать другие процессы. Это дополнительная мера, а не надёжная граница безопасности: поддержка зависит от платформы и типа бинарника.
|
||||
|
||||
### SETENV
|
||||
|
||||
```sudoers
|
||||
ada ALL=(root) SETENV: /usr/local/sbin/deploy
|
||||
```
|
||||
|
||||
`SETENV` позволяет передавать переменные окружения с меньшим числом ограничений. Это опасно для команд, поведение которых зависит от `PATH`, загрузчиков библиотек, интерпретаторов и конфигурационных переменных.
|
||||
|
||||
## Defaults
|
||||
|
||||
Директивы `Defaults` управляют поведением `sudo`.
|
||||
|
||||
```sudoers
|
||||
Defaults env_reset
|
||||
Defaults use_pty
|
||||
Defaults timestamp_timeout=5
|
||||
Defaults passwd_tries=3
|
||||
```
|
||||
|
||||
Часто используемые параметры:
|
||||
|
||||
| Параметр | Назначение |
|
||||
|---|---|
|
||||
| `env_reset` | оставляет ограниченный набор переменных окружения |
|
||||
| `secure_path` | задаёт доверенный `PATH` для команд через `sudo` |
|
||||
| `use_pty` | запускает команду в псевдотерминале |
|
||||
| `timestamp_timeout` | срок действия успешной аутентификации в минутах |
|
||||
| `passwd_tries` | число попыток ввода пароля |
|
||||
| `log_input`, `log_output` | запись ввода и вывода поддерживаемых сессий |
|
||||
|
||||
Пример `secure_path`:
|
||||
|
||||
```sudoers
|
||||
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/bin"
|
||||
```
|
||||
|
||||
Настройки можно ограничить пользователем, группой, хостом или командой:
|
||||
|
||||
```sudoers
|
||||
Defaults:ada timestamp_timeout=0
|
||||
Defaults:%ops use_pty
|
||||
Defaults!/usr/bin/systemctl !log_output
|
||||
```
|
||||
|
||||
`timestamp_timeout=0` требует пароль для каждого отдельного запуска. Отрицательное значение создаёт сессию без ограничения по времени и обычно нежелательно.
|
||||
|
||||
## Запуск от имени другого пользователя
|
||||
|
||||
```sudoers
|
||||
ada ALL=(postgres) /usr/bin/psql
|
||||
```
|
||||
|
||||
Использование:
|
||||
|
||||
```bash
|
||||
sudo -u postgres /usr/bin/psql
|
||||
```
|
||||
|
||||
С указанием группы:
|
||||
|
||||
```sudoers
|
||||
ada ALL=(deploy:deploy) /usr/local/bin/release
|
||||
```
|
||||
|
||||
## Запреты
|
||||
|
||||
Команду можно исключить через `!`:
|
||||
|
||||
```sudoers
|
||||
ada ALL=(root) ALL, !/usr/bin/su, !/usr/bin/bash
|
||||
```
|
||||
|
||||
Такой список не создаёт безопасное ограничение полного административного доступа. Пользователь с разрешением `ALL` часто может получить оболочку другим способом: через редактор, интерпретатор, отладчик, файловый менеджер, загрузку модуля, изменение исполняемого файла или сервисной конфигурации.
|
||||
|
||||
Отрицания полезны для уточнения узкого списка, но не должны использоваться как sandbox.
|
||||
|
||||
## Опасные категории команд
|
||||
|
||||
Нельзя считать ограниченными команды, которые позволяют:
|
||||
|
||||
- запустить оболочку или произвольный процесс;
|
||||
- выполнять код или загружать модули;
|
||||
- редактировать произвольные файлы;
|
||||
- менять владельца, права или ACL;
|
||||
- записывать в исполняемые файлы и каталоги из `PATH`;
|
||||
- менять unit-файлы, cron-задачи, PAM, загрузчик или конфигурацию сервисов;
|
||||
- читать секреты и закрытые ключи;
|
||||
- управлять контейнерами или виртуальными машинами с доступом к хосту.
|
||||
|
||||
Примеры команд, которые часто позволяют выйти к root:
|
||||
|
||||
```text
|
||||
sh, bash, zsh, python, ruby, perl, vim, nvim, less, find,
|
||||
tar, rsync, awk, sed, env, systemctl, docker, podman
|
||||
```
|
||||
|
||||
Риск зависит от разрешённых аргументов, файловой системы и окружения. Само имя команды недостаточно для оценки безопасности.
|
||||
|
||||
## Безопасная автоматизация
|
||||
|
||||
Предпочтительный подход — разрешить один root-owned wrapper с фиксированным поведением:
|
||||
|
||||
```sudoers
|
||||
deploy ALL=(root) NOPASSWD: /usr/local/sbin/restart-myapp
|
||||
```
|
||||
|
||||
Требования к wrapper:
|
||||
|
||||
- файл и родительские каталоги не доступны на запись вызывающему пользователю;
|
||||
- используются абсолютные пути;
|
||||
- входные параметры проверяются по allowlist;
|
||||
- не выполняются строки через shell без необходимости;
|
||||
- окружение очищается или задаётся явно;
|
||||
- временные файлы создаются безопасно;
|
||||
- логируются значимые действия.
|
||||
|
||||
Если пользователь может изменить разрешённый скрипт, библиотеку, конфигурацию или исполняемый файл, правило фактически разрешает выполнение произвольного кода с повышенными правами.
|
||||
|
||||
## Проверка правил
|
||||
|
||||
Показать доступные текущему пользователю команды:
|
||||
|
||||
```bash
|
||||
sudo -l
|
||||
```
|
||||
|
||||
Проверить другого пользователя от root:
|
||||
|
||||
```bash
|
||||
sudo -l -U ada
|
||||
```
|
||||
|
||||
Сбросить сохранённую аутентификацию:
|
||||
|
||||
```bash
|
||||
sudo -k
|
||||
```
|
||||
|
||||
Удалить timestamp полностью:
|
||||
|
||||
```bash
|
||||
sudo -K
|
||||
```
|
||||
|
||||
Проверить конфигурацию:
|
||||
|
||||
```bash
|
||||
sudo visudo -c
|
||||
```
|
||||
|
||||
Проверять нужно не только синтаксис, но и фактический результат `sudo -l`: правила могут объединяться, а более широкое разрешение в другом файле отменяет ожидаемое ограничение.
|
||||
|
||||
## Приоритет и объединение правил
|
||||
|
||||
`sudoers` не работает как обычный firewall с простым правилом «первое совпадение победило». Для пользователя могут совпасть несколько записей; разрешения и теги вычисляются по правилам sudoers и могут объединяться.
|
||||
|
||||
Практические следствия:
|
||||
|
||||
- узкое правило не отменяет широкое разрешение из другого файла;
|
||||
- порядок важен для некоторых тегов и параметров;
|
||||
- членство пользователя в нескольких административных группах нужно учитывать;
|
||||
- проверять итог следует через `visudo -c` и `sudo -l`.
|
||||
|
||||
## Минимальный пример
|
||||
|
||||
Задача: разрешить группе `webops` проверять и перезапускать только `nginx`.
|
||||
|
||||
```sudoers
|
||||
Cmnd_Alias NGINX_READ = \
|
||||
/usr/bin/systemctl status nginx
|
||||
|
||||
Cmnd_Alias NGINX_WRITE = \
|
||||
/usr/bin/systemctl reload nginx, \
|
||||
/usr/bin/systemctl restart nginx
|
||||
|
||||
%webops ALL=(root) NGINX_READ
|
||||
%webops ALL=(root) PASSWD: NGINX_WRITE
|
||||
```
|
||||
|
||||
Проверка:
|
||||
|
||||
```bash
|
||||
sudo visudo -c
|
||||
sudo -l
|
||||
sudo /usr/bin/systemctl status nginx
|
||||
```
|
||||
|
||||
## Чек-лист
|
||||
|
||||
- Правило хранится в отдельном файле `/etc/sudoers.d/`.
|
||||
- Файл проверен через `visudo`.
|
||||
- Использованы абсолютные пути.
|
||||
- Разрешены конкретные команды и аргументы.
|
||||
- Нет лишнего `ALL`.
|
||||
- `NOPASSWD` используется только при необходимости.
|
||||
- Пользователь не может изменить разрешённую команду или её зависимости.
|
||||
- Не разрешены оболочки, редакторы и интерпретаторы без осознанной причины.
|
||||
- Учтены переменные окружения и `secure_path`.
|
||||
- Итоговые права проверены через `sudo -l`.
|
||||
- Есть журналирование и способ аварийного получения root-доступа.
|
||||
|
||||
## Связанные заметки
|
||||
|
||||
- [[Super-user]]
|
||||
- [[File Permissions]]
|
||||
- [[Linux - MOC]]
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: Defense-in-depth
|
||||
status: seed
|
||||
type: concept
|
||||
tags:
|
||||
- networking
|
||||
- security
|
||||
- infosec
|
||||
- defense-in-depth
|
||||
created: 2026-06-16
|
||||
updated: 2026-06-16
|
||||
aliases:
|
||||
- DiD
|
||||
- Глубокоэшелонированная защита
|
||||
source: "[[Безопасность инфраструктуры. ZTNA, SASE, DiD]]"
|
||||
---
|
||||
# Defense-in-depth
|
||||
|
||||
**Defense-in-depth** (**DiD**) — глубокоэшелонированная защита. Идея в том, чтобы строить несколько независимых слоёв контроля, которые замедляют злоумышленника и ограничивают ущерб при компрометации одного слоя.
|
||||
|
||||
Концепция заимствована из военной стратегии: защита не должна зависеть от одного барьера.
|
||||
|
||||
## Контуры защиты
|
||||
|
||||
DiD делит защиту на три группы.
|
||||
|
||||
**Физическая защита**:
|
||||
|
||||
- охрана и охранные системы;
|
||||
- СКУД;
|
||||
- видеонаблюдение;
|
||||
- сигнализация;
|
||||
- закрытые помещения, шкафы, замки, сейфы.
|
||||
|
||||
**Техническая защита**:
|
||||
|
||||
- контроль сетевого доступа;
|
||||
- межсетевые экраны;
|
||||
- антивирусная защита;
|
||||
- прокси-серверы;
|
||||
- системы аутентификации и авторизации.
|
||||
|
||||
**Административная защита**:
|
||||
|
||||
- политики доступа;
|
||||
- регламенты обработки секретов;
|
||||
- списки разрешённого и запрещённого ПО;
|
||||
- правила работы с гостями, подрядчиками и внешними организациями.
|
||||
|
||||
## Практические направления
|
||||
|
||||
- ограничение привилегированного доступа;
|
||||
- observability и аудит событий;
|
||||
- культура DevSecOps;
|
||||
- MFA;
|
||||
- модели нулевого доверия;
|
||||
- [[SASE]];
|
||||
- централизованное управление секретами;
|
||||
- безопасные практики разработки;
|
||||
- регулярное сканирование инфраструктуры;
|
||||
- сервисные сетки и [[Сегментация сети|сегментация]].
|
||||
|
||||
## Связь с ZTNA и SASE
|
||||
|
||||
[[Zero Trust Network Access|ZTNA]], [[SASE]] и DiD не конкурируют друг с другом. DiD задаёт общий принцип многоуровневой защиты, ZTNA описывает модель доступа, а SASE объединяет сетевой доступ и защитные сервисы в единую архитектуру.
|
||||
|
||||
## Связанные заметки
|
||||
|
||||
- [[Введение в сетевую безопасность]]
|
||||
- [[Zero Trust Network Access]]
|
||||
- [[SASE]]
|
||||
- [[Сегментация сети]]
|
||||
- [[Веб-аутентификация и авторизация]]
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
title: SASE
|
||||
status: seed
|
||||
type: concept
|
||||
tags:
|
||||
- networking
|
||||
- security
|
||||
- infosec
|
||||
- sase
|
||||
created: 2026-06-16
|
||||
updated: 2026-06-16
|
||||
aliases:
|
||||
- Secure Access Service Edge
|
||||
source: "[[Безопасность инфраструктуры. ZTNA, SASE, DiD]]"
|
||||
---
|
||||
# SASE
|
||||
|
||||
**SASE** (**Secure Access Service Edge**) — подход на стыке сетевого подключения и информационной безопасности. Его цель — дать безопасный и оптимальный доступ к IT-ресурсам независимо от того, где находятся пользователь, устройство и приложение.
|
||||
|
||||
Концепция SASE была сформулирована Gartner в 2019 году. Это не один продукт, а архитектурная идея конвергенции сетевого доступа и защитных сервисов.
|
||||
|
||||
## Предпосылки
|
||||
|
||||
- размытие сетевого периметра;
|
||||
- рост облачных сервисов;
|
||||
- удалённая работа;
|
||||
- использование личных устройств;
|
||||
- необходимость сочетать безопасность и качество канала.
|
||||
|
||||
## Базовые компоненты
|
||||
|
||||
- **[[Zero Trust Network Access|ZTNA]]** — доступ по модели нулевого доверия;
|
||||
- **SD-WAN** — программно-управляемый выбор канала;
|
||||
- **SWG** — безопасный веб-шлюз;
|
||||
- **CASB** — брокер безопасного доступа к облачным сервисам;
|
||||
- **FWaaS** — межсетевой экран как сервис.
|
||||
|
||||
## Дополнительные компоненты
|
||||
|
||||
Вендоры могут включать в SASE дополнительные механизмы:
|
||||
|
||||
- DLP;
|
||||
- QoS;
|
||||
- NGFW;
|
||||
- [[VPN]];
|
||||
- UEBA;
|
||||
- антифрод;
|
||||
- защиту DNS и Wi-Fi;
|
||||
- инструменты обфускации.
|
||||
|
||||
## Связь с другими подходами
|
||||
|
||||
SASE включает [[Zero Trust Network Access|ZTNA]] как один из базовых компонентов. В рамках [[Defense-in-depth]] SASE можно рассматривать как один из архитектурных слоёв защиты доступа и сетевого трафика.
|
||||
|
||||
## Связанные заметки
|
||||
|
||||
- [[Введение в сетевую безопасность]]
|
||||
- [[Zero Trust Network Access]]
|
||||
- [[Defense-in-depth]]
|
||||
- [[VPN]]
|
||||
- [[Сегментация сети]]
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
title: Zero Trust Network Access
|
||||
status: seed
|
||||
type: concept
|
||||
tags:
|
||||
- networking
|
||||
- security
|
||||
- infosec
|
||||
- ztna
|
||||
created: 2026-06-16
|
||||
updated: 2026-06-16
|
||||
aliases:
|
||||
- ZTNA
|
||||
source: "[[Безопасность инфраструктуры. ZTNA, SASE, DiD]]"
|
||||
---
|
||||
# Zero Trust Network Access
|
||||
|
||||
**Zero Trust Network Access** (**ZTNA**) — модель доступа к ресурсам на основе принципа нулевого доверия. Решение о доступе принимается при каждом обращении к ресурсу, а не один раз после подключения к «доверенной» сети.
|
||||
|
||||
ZTNA не отменяет модель угроз, а меняет её: базовый сценарий предполагает, что сеть, устройство или пользователь уже могут быть скомпрометированы.
|
||||
|
||||
## Нулевое доверие
|
||||
|
||||
Концепция нулевого доверия исходит из того, что доверенных подключений нет. Доступ должен проверяться постоянно, с учётом контекста запроса, состояния пользователя, устройства и ресурса.
|
||||
|
||||
ZTNA часто выступает рыночной упаковкой Zero Trust, но сама концепция не привязана к конкретному вендору или технологии.
|
||||
|
||||
## Сценарии применения
|
||||
|
||||
- удалённый доступ с личных устройств;
|
||||
- гостевой, подрядный и временный доступ;
|
||||
- контроль IoT-устройств;
|
||||
- доступ к внутренним ресурсам без прямого расширения периметра через [[VPN]].
|
||||
|
||||
## Контексты доступа
|
||||
|
||||
Для принятия решения ZTNA может учитывать:
|
||||
|
||||
- сетевой контекст: тип подключения, геолокация, источник запроса;
|
||||
- контекст устройства: модель, ОС, обновления, состояние безопасности;
|
||||
- пользовательский контекст: идентификация и аутентификация;
|
||||
- ролевой контекст: авторизация и необходимые ресурсы;
|
||||
- контекст безопасности: признаки компрометации, риск запроса, состояние учётной записи.
|
||||
|
||||
## Внедрение
|
||||
|
||||
Перед внедрением нужно определить сценарии применения, роли пользователей, ресурсы, контексты доступа и оценку рисков.
|
||||
|
||||
Практические первые шаги:
|
||||
|
||||
1. Включить MFA там, где это применимо.
|
||||
2. Отказаться от статичного доверия к IP-адресам и allowlist.
|
||||
3. Идентифицировать каждого пользователя и каждый запрос.
|
||||
4. Сегментировать ресурсы и доступы к ним.
|
||||
5. Постепенно убрать разделение пользователей на «своих» и «чужих».
|
||||
|
||||
## Связанные заметки
|
||||
|
||||
- [[Введение в сетевую безопасность]]
|
||||
- [[SASE]]
|
||||
- [[Defense-in-depth]]
|
||||
- [[VPN]]
|
||||
- [[Веб-аутентификация и авторизация]]
|
||||
- [[Сегментация сети]]
|
||||
@@ -1,20 +1,23 @@
|
||||
---
|
||||
status: seed
|
||||
title: Введение в сетевую безопасность
|
||||
status: processing
|
||||
type: concept
|
||||
tags:
|
||||
- networking
|
||||
- security
|
||||
- infosec
|
||||
- vulnerability-scanning
|
||||
created: 2025-12-17
|
||||
updated: 2026-05-07
|
||||
updated: 2026-06-16
|
||||
aliases:
|
||||
- Введение в сетевую безопасность
|
||||
source: "[[Безопасность инфраструктуры. ZTNA, SASE, DiD]]"
|
||||
---
|
||||
# Введение в сетевую безопасность
|
||||
|
||||
**Сетевая безопасность** — часть информационной безопасности, которая защищает сетевую инфраструктуру, передаваемые данные и доступ к сетевым ресурсам.
|
||||
|
||||
Сетевая безопасность связана с архитектурой сети, политиками доступа, сегментацией, мониторингом и защитой от атак. В контексте сети рядом лежат темы [[Сегментация сети]], [[VPN]] и [[Networking - MOC]].
|
||||
Сетевая безопасность связана с архитектурой сети, политиками доступа, [[Сегментация сети|сегментацией]], мониторингом и защитой от атак. В контексте сети рядом лежат темы [[VPN]], [[Веб-аутентификация и авторизация]] и [[Networking - MOC]].
|
||||
|
||||
## CIA: базовая модель безопасности
|
||||
|
||||
@@ -67,8 +70,42 @@ aliases:
|
||||
3. Отпечаток сравнивается с базой известных уязвимостей.
|
||||
4. Формируется отчёт с найденными проблемами.
|
||||
|
||||
Сканеры инфраструктуры помогают находить слабые шифры, небезопасные протоколы, дефолтные пароли, открытые порты, устаревшие приложения, просроченные сертификаты и ошибки конфигурации. Практический смысл отчёта — не сам список находок, а приоритизация рисков и митигация по рекомендациям инструмента.
|
||||
|
||||
OpenVAS, современная ветка которого связана с Greenbone, можно развернуть в контейнере для учебной проверки подконтрольной инфраструктуры. Типовой сценарий:
|
||||
|
||||
1. Запустить OpenVAS/Greenbone.
|
||||
2. Создать target с адресом проверяемого узла.
|
||||
3. Создать scan task.
|
||||
4. Дождаться статуса `Done`.
|
||||
5. Разобрать findings и поле `Solution`.
|
||||
|
||||
## Архитектурные подходы
|
||||
|
||||
Отдельные подходы к защите инфраструктуры:
|
||||
|
||||
- [[Zero Trust Network Access]] — модель доступа на основе нулевого доверия.
|
||||
- [[SASE]] — конвергенция сетевого доступа и защитных сервисов.
|
||||
- [[Defense-in-depth]] — многоуровневая защита, где компрометация одного слоя не должна ломать всю систему.
|
||||
|
||||
## Источники
|
||||
|
||||
- [Базовое описание ZTNA](https://codeby.net/threads/chto-takoye-ztna-i-zachem-on-nuzhen.84541/)
|
||||
- [Zero Trust архитектура в 2025 году](https://securitymedia.org/info/zero-trust-arkhitektura-v-2025-godu-printsipy-kontseptsii-nulevogo-doveriya-i-ee-razvitie.html)
|
||||
- [Практические рекомендации по переходу к Zero Trust](https://www.kaspersky.ru/blog/zero-trust-transition-practical-advice/39484/)
|
||||
- [SASE и ИИ](https://habr.com/ru/articles/914496/)
|
||||
- [Defense-in-depth на примере XZ Utils](https://www.wiz.io/academy/defense-in-depth/)
|
||||
- [Defense-in-depth: уровни контроля](https://www.wallarm.com/what/defense-in-depth-concept/)
|
||||
- [Defense-in-depth](https://www.fortinet.com/resources/cyberglossary/defense-in-depth/)
|
||||
- [Modern defense-in-depth strategies](https://www.isaca.org/resources/news-and-trends/isaca-now-blog/2025/beyond-the-moat-modern-defense-in-depth-strategies/)
|
||||
|
||||
## Связанные заметки
|
||||
|
||||
- [[Networking - MOC]]
|
||||
- [[Сегментация сети]]
|
||||
- [[VPN]]
|
||||
- [[Веб-аутентификация и авторизация]]
|
||||
- [[CA и NGINX]]
|
||||
- [[Zero Trust Network Access]]
|
||||
- [[SASE]]
|
||||
- [[Defense-in-depth]]
|
||||
|
||||
@@ -8,7 +8,7 @@ tags:
|
||||
- adaptive-design
|
||||
- responsive-web
|
||||
created: 2026-05-30
|
||||
updated: 2026-06-05
|
||||
updated: 2026-06-07
|
||||
source:
|
||||
- '[[14-css-adaptive.pdf]]'
|
||||
- '[[17-css-grid.pdf]]'
|
||||
@@ -16,8 +16,16 @@ source:
|
||||
aliases:
|
||||
- 'CSS: Адаптивность, Flexbox и Grid'
|
||||
---
|
||||
|
||||
# Адаптивная верстка
|
||||
|
||||
## Виды верстки
|
||||
1) **Фиксированная** - макет разрабатывается с фиксированными параметрами (например, с точным указанием размеров в пикселях). Такой подход *устарел* и применяется редко.
|
||||
2) **Мобильная** - сайт создается на выделенном поддомене и адаптируется только под мобильные устройства. Соответственно, ресурс станет *удобен для чтения лишь пользователям смартфонов и планшетов.*
|
||||
3) **Резиновая** - ширина блока задается в процентах от ширины экрана пользователя. Благодаря этому "резиновые" сайты одинаково хорошо читаются с любых устройств. Тем не менее, адаптивная верстка, равно как и отзывчивая, постепенно вытесняет резиновую.
|
||||
4) **Отзывчивая** - напоминает адаптивную верстку, но в то же время принципиально отличается от нее. Отличие заключается в действиях на брейк-поинтах. Если в адаптивном варианте браузер резко меняет сверстанный макет, то в отзывчивом происходит плавное изменение верстки между точками слома с максимальным сохранением стабильного вида страницы. Фактически данный подход сочетает в себе признаки адаптивности и "резиновости".
|
||||
5) **Адаптивная** - Основные параметры сайта подстраиваются под характеристики пользовательского устройства. Ресурс в результате должен удобно читаться практически с любого гаджета или компьютера.
|
||||
|
||||
Адаптивность (AWD) и отзывчивость (RWD) позволяют сайту корректно отображаться на любых устройствах.
|
||||
|
||||
## Медиазапросы (@media)
|
||||
@@ -30,6 +38,18 @@ aliases:
|
||||
- **Mobile First:** Сначала стили для мобильных, затем `min-width`.
|
||||
- **Desktop First:** Сначала для десктопов, затем `max-width`.
|
||||
|
||||
```css
|
||||
/* Различается и ориентация макета */
|
||||
@media (orientation: landscape) {
|
||||
/* При горизонтальной ориентации фоновым цветом сайта будет белый */
|
||||
body {
|
||||
background: #FFF;
|
||||
}
|
||||
}
|
||||
/* Ещё существует portrait - вертикальная */
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Flexbox (Flexible Box Layout)
|
||||
|
||||
@@ -8,7 +8,7 @@ tags:
|
||||
- tables
|
||||
- forms
|
||||
created: 2026-05-30
|
||||
updated: 2026-06-05
|
||||
updated: 2026-06-07
|
||||
title: "Основы HTML (структура, таблицы, формы)"
|
||||
source:
|
||||
- '[[10-html_основы_НП.pdf]]'
|
||||
@@ -17,6 +17,7 @@ source:
|
||||
aliases:
|
||||
- Основы HTML (структура, таблицы, формы)
|
||||
---
|
||||
|
||||
# Основы HTML
|
||||
|
||||
**HTML (Hypertext Markup Language)** — язык разметки для структурирования и отображения веб-страниц.
|
||||
@@ -170,6 +171,21 @@ aliases:
|
||||
```
|
||||
- **Объединение ячеек:** `colspan` (по горизонтали), `rowspan` (по вертикали).
|
||||
|
||||
**Границы таблицы** задаются с помощью свойств `border` и `border-collapse`:
|
||||
```css
|
||||
/* внешние границы таблицы серого цвета толщиной 1px */
|
||||
table {
|
||||
border: 1px solid gray;
|
||||
border-collapse: collapse;
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
/* границы ячеек тела таблицы */
|
||||
td {
|
||||
border: 1px solid grey;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Формы
|
||||
@@ -185,7 +201,7 @@ aliases:
|
||||
</form>
|
||||
```
|
||||
|
||||
### Элементы управления:
|
||||
### Элементы управления
|
||||
- `<input>` — универсальное поле ввода. Поведение зависит от `type`.
|
||||
- `<select>` + `<option>` — выпадающий список.
|
||||
- `<textarea>` — многострочное поле для текста.
|
||||
@@ -239,7 +255,7 @@ aliases:
|
||||
<textarea id="comment" name="comment" rows="5"></textarea>
|
||||
```
|
||||
|
||||
### Атрибуты валидации:
|
||||
### Атрибуты валидации
|
||||
- `required` — поле обязательно для заполнения.
|
||||
- `minlength` — минимальная длина текста.
|
||||
- `maxlength` — максимальная длина текста.
|
||||
@@ -274,8 +290,7 @@ HTML-валидация работает в браузере до отправк
|
||||
</form>
|
||||
```
|
||||
|
||||
## Простыми словами для экзамена
|
||||
|
||||
## Прочие темы
|
||||
### Документ и `head`
|
||||
|
||||
`<head>` хранит служебную информацию, а не видимый контент. Там обычно находятся:
|
||||
|
||||
@@ -7,7 +7,7 @@ tags:
|
||||
- selectors
|
||||
- box-model
|
||||
created: 2026-05-30
|
||||
updated: 2026-06-05
|
||||
updated: 2026-06-08
|
||||
source:
|
||||
- "[[12-css-базовые_свойства.pdf]]"
|
||||
- "[[12-css-блочная_модель.pdf]]"
|
||||
@@ -45,9 +45,7 @@ aliases:
|
||||
2. ID (0, 1, 0, 0)
|
||||
3. Классы, атрибуты, псевдоклассы (0, 0, 1, 0)
|
||||
4. Теги и псевдоэлементы (0, 0, 0, 1)
|
||||
|
||||
Пример:
|
||||
|
||||
![[Pasted image 20260608083946.png]]
|
||||
```css
|
||||
body .content { color: red; }
|
||||
div p { color: blue; }
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
---
|
||||
status: seed
|
||||
type: concept
|
||||
tags: []
|
||||
created: 2025-12-17
|
||||
updated: 2026-06-16
|
||||
aliases: []
|
||||
---
|
||||
|
||||
# CI-CD Конвейер для сборки и доставки продукта. Построение пайплайнов
|
||||
## Часть 2. Пайплайны в GitlabCI
|
||||
|
||||
Пайплайны в GitlabCI строятся на основе языков **Shell** и **Yaml**. С помощью них можно описывать этапы процессов CI/CD и их желаемое поведение.
|
||||
|
||||
Язык `yaml` позволяет описывать конфигурацию этапов в декларативном ключе. Основной файл, в котором описывается работа пайплайна, носит название `Gitlab-ci.yml`.
|
||||
|
||||
### Shell
|
||||
|
||||
В GitlabCI при описании этапов пайплайна можно задействовать `shell`.
|
||||
|
||||
> **Shell** — командный интерпретатор операционных систем семейства Unix.
|
||||
|
||||
Shell позволяет существенно расширить возможности описания процессов CI/CD.
|
||||
|
||||
### Stage
|
||||
|
||||
Структура файла `Gitlab-ci.yml` предполагает разбиение пайплайна на этапы (`Stages`).
|
||||
Каждый Stage представляет из себя этап пайплайна с полным описанием его работы.
|
||||
|
||||
Этапы (Stages) могут выполняться **последовательно** или **параллельно**.
|
||||
|
||||
Так могут выглядеть типовые этапы (Stages) в пайплайне GitlabCI.
|
||||
```yaml
|
||||
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
- deploy
|
||||
|
||||
build-job:
|
||||
stage: build
|
||||
script:
|
||||
- do some build
|
||||
|
||||
test-job:
|
||||
stage: test
|
||||
script:
|
||||
- do some test
|
||||
|
||||
deploy-job:
|
||||
stage: deploy
|
||||
script:
|
||||
- do some deploy
|
||||
```
|
||||
|
||||
**Запуск пайплайна можно инициировать тремя способами:**
|
||||
|
||||
- *Вручную* (через веб-интерфейс или API).
|
||||
- *Автоматически* по событию (Push, Commit, Merge-request, etc) - как триггеры в PSQL
|
||||
- Используя *Web-hook*.
|
||||
|
||||
**Как ручной, так и автоматический запуск пайплайнов может быть дополнительно параметризован при запуске:**
|
||||
|
||||
- Указание специфических переменных.
|
||||
- Выбор учетных данных (Credentials).
|
||||
- Сборка по конкретному коммиту, ветке, тэгу.
|
||||
- .. и многое другое
|
||||
|
||||
**Вложенные пайплайны:**
|
||||
|
||||
В зависимости от сложности процессов CI/CD, пайплайны могут быть составными — содержать вложенные пайплайны, выполняющиеся последовательно или параллельно.
|
||||
|
||||
**Интеграция:**
|
||||
|
||||
В случае использования в процессах CI/CD нескольких инструментов, возможна их интеграция (к примеру, исходный код хранится в gitlab, а пайплайны запускаются в Jenkins) через встроенный в GitlabCI механизм интеграций.
|
||||
|
||||
**GitlabCI имеет понятный веб интерфейс, в котором отображаются** **результаты прохождения пайплайнов и их отдельных этапов** **в простом и доступном виде:**
|
||||
|
||||

|
||||
|
||||
## Часть 3. Пайплайны в Jenkins
|
||||
|
||||
Для полноценной работы Jenkins будет достаточно предустановленных плагинов, но наличие минимально необходимых дополнительных плагинов существенно **облегчит** построение и модификацию пайплайнов.
|
||||
|
||||

|
||||
|
||||
Не забывайте своевременно производить обновление используемых плагинов, чтобы избежать ошибок и уязвимостей.
|
||||
|
||||

|
||||
|
||||
После установки плагина, его требуется настроить для работы, сделать это можно в настройках Jenkins:
|
||||
|
||||
- Глобальные настройки.
|
||||
- Настройки инструментов.
|
||||
- Настройки безопасности.
|
||||
- Конфигурирование сред исполнения.
|
||||
|
||||

|
||||
|
||||
Настройки проекта в Jenkins сводятся к конфигурации самого проекта и его интеграций, а также настройке пайплайна.
|
||||
|
||||
Для описания поведения пайплайна мы можем использовать графический интерфейс или язык **Groovy**.
|
||||
|
||||

|
||||
|
||||
Если вы испытываете трудности с языком Groovy — вам придет на помощь встроенный редактор запросов.
|
||||
|
||||
Сгенерированный запрос можно использовать в описании вашего пайплайна.
|
||||
|
||||

|
||||
|
||||
Хорошей практикой является описание пайплайна в отдельном файле, который хранится не в Jenkins и подвергается защите от несанкционированного внесения изменений.
|
||||
|
||||
Такой файл часто носит название Jenkinsfile и хранится в репозитории (например, в Gitlab), а не в самом Jenkins.
|
||||
|
||||

|
||||
|
||||
Хранящийся в репозитории Jenkinsfile, вместе с самим исходным кодом продукта, защищен от изменений (их могут вносить только уполномоченные члены команды или с использованием процедуры Merge-request).
|
||||
|
||||

|
||||
|
||||
Инициировать процедуру запуска пайплайна можно несколькими способами:
|
||||
|
||||
- Вручную (веб-интерфейс или API).
|
||||
- Автоматически, с использованием интеграций (посредством плагинов).
|
||||
- Используя Web-hook.
|
||||
- С помощью планировщика сборок.
|
||||
|
||||

|
||||
|
||||
Имея опыт работы с языком **Groovy**, используя разнообразные плагины и следуя хорошим практикам организации пайплайнов, можно добиться грамотного построения процессов CI/CD:
|
||||
|
||||
- Параметризованные сборки.
|
||||
- Хранение Jenkinsfile в репозитории с ограничением на изменение.
|
||||
- Интеграция с Git, Kubernetes, Инструментами обеспечения информационной безопасности и Observability.
|
||||
- Отказоустойчивая и безопасная конфигурация доступа к Jenkins для других инструментов.
|
||||
- Визуализация процессов.
|
||||
|
||||
## Часть 4. Внедрение и развитие процессов CI/CD
|
||||
|
||||
## Внедрение и развитие процессов CI/CD
|
||||
|
||||
В зависимости от того, в каком состоянии находятся существующие процессы разработки, тестирования и администрирования, процессы CI/CD должны внедряться предсказуемо и поэтапно.
|
||||
|
||||
Любые существующие процессы CI/CD должны развиваться не прекращаясь.
|
||||
|
||||
**Обогатить имеющиеся процессы непрерывной сборки и доставки** **можно следующими практиками:**
|
||||
|
||||
- IaC.
|
||||
- Orchestration.
|
||||
- Observability.
|
||||
- Info-security.
|
||||
|
||||
При настройке процессов непрерывной доставки используйте подход декларативного описания инфраструктуры и конфигурации в виде кода (практики IaC).
|
||||
|
||||
Старайтесь придерживаться микросервисной архитектуры там, где это оправдано.
|
||||
|
||||
Ориентируйтесь на контейнерные технологии.
|
||||
|
||||
Изучайте и применяйте технологии оркестрации контейнерных и неконтейнерных рабочих нагрузок в режиме кластера.
|
||||
|
||||
**Не пренебрегайте развитием практик обеспечения Observability:**
|
||||
|
||||
- Мониторинг.
|
||||
- Трассировки.
|
||||
- Аудит.
|
||||
- Журналирование.
|
||||
- Обработка логов.
|
||||
- Сбор обратной связи.
|
||||
|
||||
Дополняйте этими практиками этапы своих пайплайнов, или добавляйте новые этапы на их основе.
|
||||
|
||||
**Существующие практики CI/CD можно и нужно обогащать** **внедрением проверок по информационной безопасности из** **методологии Devsecops:**
|
||||
|
||||
- Различные виды анализа (SAST, SCA, DAST, IAST, RASP).
|
||||
- Сканирование инфраструктуры.
|
||||
- Использование профильных инструментов для работы с сенсетивной информацией.
|
||||
|
||||
Помните о том, что процессы CI/CD — это лишь одна из множества Devops-практик, которые важно и нужно применять для создания благоприятной атмосферы в коллективной работе над цифровым продуктом.
|
||||
|
||||
**Devops** — это, в первую очередь, культура взаимодействия всех участников жизненного цикла цифрового продукта.
|
||||
@@ -0,0 +1,201 @@
|
||||
---
|
||||
status: seed
|
||||
type: concept
|
||||
tags: []
|
||||
created: 2025-12-17
|
||||
updated: 2026-06-10
|
||||
aliases: []
|
||||
---
|
||||
|
||||
# CIСD Конвейер для сборки и доставки продукта. Знакомство с GitlabCI и Jenkins
|
||||
|
||||
## Процессы CI/CD
|
||||
|
||||
**CI/CD (Continuous integration and Continuous delivery)** — широко распространенная devops-практика, подразумевающая непрерывную сборку и доставку цифрового продукта до конечной инфраструктуры.
|
||||
|
||||
### Continuous integration
|
||||
|
||||
Методология, при которой изменения в исходный код вносятся последовательно и непрерывно.
|
||||
|
||||
Исходный код подвергается проверкам, тестируется, упаковывается в артефакт, помещается в хранилище и готовится к отправке на развертывание.
|
||||
|
||||
### Continuous delivery
|
||||
|
||||
Практика, являющаяся логическим продолжением непрерывной сборки (интеграции) цифрового продукта.
|
||||
|
||||
Артефакт, прошедший все этапы непрерывной сборки, немедленно развертывается на конечной инфраструктуре, где в виде цифрового продуктаьвыполняет свои функции.
|
||||
|
||||
**Цели CI/CD:**
|
||||
|
||||
- Обеспечить последовательный и непрерывный процесс сборки, проверки, тестирования и развертывания цифровых продуктов.
|
||||
- Автоматизировать процедуру сборки и доставки.
|
||||
- Минимизировать возможные ошибки и облегчить процесс их исправления.
|
||||
- Поддержать современный взгляд на гибкие методологии разработки.
|
||||
|
||||
**Преимущества CI/CD:**
|
||||
|
||||
- Максимальная автоматизация процессов.
|
||||
- Своевременное обнаружение ошибок.
|
||||
- Сокращение цикла получения обратной связи.
|
||||
- Разделение сред разработки, тестирования и эксплуатации цифрового продукта.
|
||||
- Глубокая наблюдаемость (Observability).
|
||||
|
||||
**Распространенные этапы CI/CD:**
|
||||
|
||||
- Создание кода / интеграция функционала.
|
||||
- Модульное тестирование.
|
||||
- Сборка / компилирование.
|
||||
- Доставка артефакта и его развертывание.
|
||||
- Дополнительные проверки.
|
||||
- Сбор метрик / сбор обратной связи.
|
||||
|
||||
**Начальный этап:**
|
||||
|
||||
Этап создания кода (интеграции нового функционала) подразумевает наличие системы контроля версий, где осуществляется коллективная работа над исходным кодом цифрового продукта и ведется разработка функционала (исправление ошибок).
|
||||
|
||||
В процессах CI/CD это начальный этап сборки и доставки.
|
||||
|
||||
**Автоматизация:**
|
||||
|
||||
Модульное тестирование относится к логическим проверкам исходного кода, не подразумевающее запуск приложения на исполнение.
|
||||
|
||||
В первую очередь необходимо самим разработчикам для уверенности в отсутствии логических ошибок в разрабатываемых участках исходного кода.
|
||||
|
||||
Процессы CI/CD позволяют автоматизировать запуск таких проверок.
|
||||
|
||||
**Сборка:**
|
||||
|
||||
Сборка (компилирование) продукта из исходного кода чаще всего осуществляется с применением стандартизированной упаковки приложения, готового к запуску на конечной инфраструктуре.
|
||||
|
||||
Сборка часто является заключительным этапом процесса CI, далее собранный и готовый к запуску *артефакт* передается процессу непрерывной доставки (но могут иметь место и дополнительные проверки).
|
||||
|
||||
**Доставка:**
|
||||
|
||||
Доставка собранного и проверенного артефакта до конечной инфраструктуры осуществляется в процессе CD.
|
||||
|
||||
Доставленный и развернутый артефакт является конечным продуктом, функции которого эксплуатируют потребители.
|
||||
|
||||
**Проверка:**
|
||||
|
||||
В зависимости от среды, до которой был доставлен артефакт, он может быть подвергнут дополнительным проверкам, прежде чем отправиться в эксплуатацию.
|
||||
|
||||
Различают несколько различных видов сред цифрового продукта:
|
||||
|
||||
- Среда разработки (Dev).
|
||||
- Среда тестирования (Test, QA, Stage, Integration, Pre-production, Uat,..).
|
||||
- Среда эксплуатации (Prod).
|
||||
|
||||
**После прохождения всех этапов процессы CI/CD не заканчиваются:**
|
||||
|
||||
- Важно собирать обратную связь о процессе эксплуатации продукта (согласно потребительским метрикам).
|
||||
- Собирать статистику (Метрики, Логи, Журналы, Трассировки).
|
||||
- Процессы CI/CD не предполагают остановки и возобновляются с каждым новым коммитом и изменением в исходном коде.
|
||||
|
||||
### Пайплайны
|
||||
|
||||
Все этапы, составляющие процессы CI/CD, называются пайплайнами (Pipelines).
|
||||
|
||||
Создание, изменение, поддержка и совершенствование пайплайнов возможны в специализированных инструментах — **сборочных конвейерах CI/CD.**
|
||||
|
||||
## Часть 3. Знакомство с GitlabCI
|
||||
|
||||
**GitlabCI (Gitlab CI/CD)** — opensource-инструмент, встроенный в систему управления версиями Gitlab (изначально выпускался как отдельный проект).
|
||||
|
||||
GitlabCI выполняет функции сборочного конвейера, позволяющего осуществлять все этапы CI/CD.
|
||||
|
||||
**Преимущества GitlabCI:**
|
||||
|
||||
- Совместимость с Gitlab.
|
||||
- Простота интеграции в рабочие процессы.
|
||||
- Развитое комьюнити.
|
||||
- Автомасштабирование.
|
||||
|
||||
GitlabCI относительно новый инструмент, быстро завоевавший популярность в сообществе.
|
||||
|
||||
Своей известностью GitlabCI обязан в первую очередь нативностью с Gitlab, достаточной функциональностью и простотой в использовании.
|
||||
|
||||
GitlabCI полностью совместим с системой управления версиями Gitlab, изначально предполагая интеграцию Gitlab в процесс непрерывной сборки (CI).
|
||||
|
||||
Однако, возможна интеграция и с другими системами управления версиями.
|
||||
|
||||
**GitlabCI обладает множеством полезных возможностей** **для улучшения процесса автоматизации сборки и доставки,** **интеграция которых в рабочий процесс не составит труда:**
|
||||
|
||||
- Отслеживание проектов и групп.
|
||||
- Тонкая настройка пайплайнов.
|
||||
- Мониторинг состояния задач в этапах CI/CD.
|
||||
- Анализ результатов выполнения этапов.
|
||||
|
||||
GitlabCI обладает встроенным механизмом по автоматическому масштабированию сред исполнения рабочих задач (Runners).
|
||||
|
||||
Выполнение задач по сборке может осуществляться на Runners под управлением ВМ на разных ОС, Docker, Kubernetes.
|
||||
|
||||
## Часть 4. Знакомство с Jenkins
|
||||
|
||||
**Jenkins** — Open-source инструмент CI/CD, предназначенный для автоматизации множества задач в цифровых проектах и являющийся сборочным конвейером.
|
||||
|
||||
**Достоинства и преимущества Jenkins:**
|
||||
|
||||
- Простота в установке, настройке и эксплуатации.
|
||||
- Расширяемость за счет использования Jenkins плагинов.
|
||||
- Развитое сообщество и богатая документация.
|
||||
|
||||
Jenkins легко устанавливать, настраивать и обновлять благодаря простым процедурам установки без лишних деталей и специфичных особенностей.
|
||||
|
||||
Jenkins обладает обширной и понятной документацией.
|
||||
|
||||
В Jenkins создана целая экосистема различных плагинов, расширяющая или модифицирующая существующий функционал, в том числе графический интерфейс.
|
||||
|
||||
Плагинов насчитывается более 1500, практически все из них бесплатны и очень просты в установке.
|
||||
|
||||
Jenkins обладает самым развитым сообществом по сравнению с другими сборочными конвейерами CI/CD.
|
||||
|
||||
К комьюнити относятся не только команда разработки самого инструмента, но и разработчики множества плагинов.
|
||||
|
||||
## Часть 5. GitlabCI vs Jenkins
|
||||
|
||||
**Сходства GitlabCI и Jenkins:**
|
||||
|
||||
- Открытый исходный код.
|
||||
- Кроссплатформенность.
|
||||
- Простота в установке и настройке.
|
||||
- Гибкая работа с пайплайнами.
|
||||
- Поддержка API.
|
||||
- Интегрируемость с другими системами в рамках рабочих процессов.
|
||||
- Обширное неравнодушное комьюнити.
|
||||
|
||||
### Возможности
|
||||
|
||||
**Уникальные возможности GitlabCI:**
|
||||
|
||||
- Нативность Gitlab.
|
||||
- Не требуется отдельная установка.
|
||||
- Собственный мониторинг производительности.
|
||||
- Встроенные решения по проверке качества кода.
|
||||
|
||||
**Уникальные возможности Jenkins:**
|
||||
|
||||
- Развитая экосистема плагинов.
|
||||
- Расширенные возможности по интеграции со множеством систем за счет использования плагинов.
|
||||
- Возможность использования языка Groovy для описания пайплайнов.
|
||||
- Более обширное комьюнити, лучше документация.
|
||||
|
||||
### Недостатки
|
||||
|
||||
**Недостатки GitlabCI:**
|
||||
|
||||
- Специфичная работа с артефактами.
|
||||
- Возможные трудности при описании и работе со сложными пайплайнами.
|
||||
- Местами чувствуется недостаточность языков описания пайплайнов.
|
||||
|
||||
**Недостатки Jenkins:**
|
||||
|
||||
- Нагромождение плагинов может вызывать сложности в их поддержке и обновлении.
|
||||
- Для небольших проектов и небольших команд работа может оказаться сложнее, чем с GitlabCI.
|
||||
|
||||
GitlabCI и Jenkins замечательные инструменты, обладающие впечатляющими возможностями.
|
||||
|
||||
Если вы используете Gitlab в своей работе, логичнее использовать GitlabCI для процессов CI/CD.
|
||||
|
||||
Если вы не привязаны к Gitlab и вас впечатляет экосистема плагинов, выбирайте Jenkins.
|
||||
|
||||
Оба инструмента прекрасно справляются со своими задачами.
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
status: seed
|
||||
type: concept
|
||||
tags: []
|
||||
created: 2025-12-17
|
||||
updated: 2026-06-15
|
||||
aliases: []
|
||||
---
|
||||
|
||||
# Proxmox VE — local, local-lvm, LVM
|
||||
|
||||
## Что такое local и local-lvm
|
||||
|
||||
|Storage|Тип|Путь / устройство|Хранит|
|
||||
|---|---|---|---|
|
||||
|`local`|directory|`/var/lib/vz`|ISO, CT-шаблоны, бэкапы, snippets|
|
||||
|`local-lvm`|LVM thin pool|`pve/data` (LV в VG `pve`)|Диски VM/CT, снапшоты дисков|
|
||||
|
||||
## Куда скачиваются ISO через веб-интерфейс
|
||||
|
||||
По умолчанию — в `local` (`/var/lib/vz/template/iso/`).
|
||||
Чтобы скачивать на другой storage — выбрать его в выпадающем списке при Download from URL.
|
||||
Условие: у storage должен быть включён Content type **ISO image** (Datacenter → Storage → Edit → Content).
|
||||
|
||||
## Проверить содержимое LVM
|
||||
|
||||
```bash
|
||||
# Список всех LV с размерами
|
||||
lvs
|
||||
|
||||
# Storage Proxmox с backing-устройствами
|
||||
pvesm status
|
||||
```
|
||||
|
||||
Thin pool определяется по атрибуту `twi` в колонке `Attr`.
|
||||
|
||||
## Удалить local-lvm и расширить root
|
||||
|
||||
> Перед удалением убедиться, что на `local-lvm` нет дисков VM/CT (Data% = 0.00).
|
||||
|
||||
### 1. Удалить thin pool
|
||||
|
||||
```bash
|
||||
sudo lvremove /dev/pve/data
|
||||
# подтвердить: y
|
||||
```
|
||||
|
||||
### 2. Расширить root на нужный размер
|
||||
|
||||
```bash
|
||||
# Добавить конкретный размер (например, 60G):
|
||||
sudo lvresize -L +60G /dev/pve/root
|
||||
|
||||
# Или отдать всё свободное место:
|
||||
sudo lvresize -l +100%FREE /dev/pve/root
|
||||
```
|
||||
|
||||
### 3. Расширить файловую систему
|
||||
|
||||
```bash
|
||||
sudo resize2fs /dev/pve/root
|
||||
```
|
||||
|
||||
Работает online, перезагрузка не нужна.
|
||||
|
||||
### 4. Удалить storage из GUI
|
||||
|
||||
Datacenter → Storage → `local-lvm` → Remove
|
||||
|
||||
### 5. Проверить результат
|
||||
|
||||
```bash
|
||||
lsblk
|
||||
lvs
|
||||
df -h /
|
||||
```
|
||||
|
||||
## Что делать с оставшимся свободным местом в VG pve
|
||||
|
||||
Нераспределённое пространство можно использовать:
|
||||
|
||||
- добавить как новый LVM storage в Proxmox
|
||||
- создать новый LV под конкретную задачу
|
||||
- оставить в резерве
|
||||
|
||||
```bash
|
||||
# Посмотреть свободное место в VG:
|
||||
vgs
|
||||
```
|
||||
@@ -0,0 +1,402 @@
|
||||
---
|
||||
title: Sudoers
|
||||
status: seed
|
||||
type: guide
|
||||
tags:
|
||||
- linux
|
||||
- sudo
|
||||
- security
|
||||
created: 2026-06-15
|
||||
updated: 2026-06-15
|
||||
aliases:
|
||||
- sudoers
|
||||
- Настройка sudo
|
||||
---
|
||||
|
||||
# Sudoers
|
||||
|
||||
`sudo` запускает команду с полномочиями другого пользователя, обычно `root`. Политика доступа задаётся в `/etc/sudoers` и дополнительных файлах из `/etc/sudoers.d/`.
|
||||
|
||||
`sudo` не является заменой файловых прав. Он выдаёт полномочия на конкретное действие после проверки пользователя, хоста, целевого пользователя, команды и параметров политики.
|
||||
|
||||
## Где хранится конфигурация
|
||||
|
||||
- `/etc/sudoers` — основной файл.
|
||||
- `/etc/sudoers.d/` — каталог для отдельных правил.
|
||||
- `/var/log/` или журнал systemd — записи о вызовах `sudo`, в зависимости от конфигурации системы.
|
||||
|
||||
Основной файл обычно содержит:
|
||||
|
||||
```sudoers
|
||||
@includedir /etc/sudoers.d
|
||||
```
|
||||
|
||||
Для локальных правил удобнее создавать отдельный файл:
|
||||
|
||||
```bash
|
||||
sudo visudo -f /etc/sudoers.d/backup
|
||||
```
|
||||
|
||||
Имена файлов в `sudoers.d` лучше делать без точки и символа `~`: некоторые реализации игнорируют такие файлы. Права должны исключать запись обычными пользователями:
|
||||
|
||||
```bash
|
||||
sudo chown root:root /etc/sudoers.d/backup
|
||||
sudo chmod 0440 /etc/sudoers.d/backup
|
||||
```
|
||||
|
||||
## Редактирование через visudo
|
||||
|
||||
Не следует редактировать `/etc/sudoers` обычным редактором. `visudo` блокирует файл от одновременного изменения и проверяет синтаксис перед сохранением.
|
||||
|
||||
```bash
|
||||
sudo visudo
|
||||
sudo visudo -f /etc/sudoers.d/backup
|
||||
sudo visudo -c
|
||||
```
|
||||
|
||||
Если ошибка уже внесена, исправление потребует действующей root-сессии, загрузки в recovery mode или другого способа получить root-доступ.
|
||||
|
||||
Редактор можно выбрать переменными окружения:
|
||||
|
||||
```bash
|
||||
sudo EDITOR=nvim visudo
|
||||
```
|
||||
|
||||
Возможность выбора редактора зависит от настроек `sudo`; небезопасные переменные окружения могут быть отброшены.
|
||||
|
||||
## Формат правила
|
||||
|
||||
Базовая форма:
|
||||
|
||||
```sudoers
|
||||
пользователь хост=(целевой_пользователь:целевая_группа) теги: команды
|
||||
```
|
||||
|
||||
Пример:
|
||||
|
||||
```sudoers
|
||||
ada ALL=(root) /usr/bin/systemctl restart nginx
|
||||
```
|
||||
|
||||
Значение полей:
|
||||
|
||||
| Поле | Смысл |
|
||||
|---|---|
|
||||
| `ada` | кому разрешено использовать правило |
|
||||
| `ALL` | на каких хостах действует правило |
|
||||
| `(root)` | от имени какого пользователя разрешён запуск |
|
||||
| `/usr/bin/systemctl restart nginx` | разрешённая команда и аргументы |
|
||||
|
||||
Группа указывается с `%`:
|
||||
|
||||
```sudoers
|
||||
%wheel ALL=(ALL:ALL) ALL
|
||||
```
|
||||
|
||||
Это разрешает членам группы `wheel` запускать любые команды от имени любого пользователя и группы. Такое правило выдаёт полный административный доступ.
|
||||
|
||||
## Команды и аргументы
|
||||
|
||||
В правилах следует использовать абсолютный путь:
|
||||
|
||||
```sudoers
|
||||
ada ALL=(root) /usr/bin/systemctl status nginx
|
||||
```
|
||||
|
||||
Путь можно узнать командой:
|
||||
|
||||
```bash
|
||||
command -v systemctl
|
||||
```
|
||||
|
||||
Аргументы являются частью разрешения. Эти правила различаются:
|
||||
|
||||
```sudoers
|
||||
ada ALL=(root) /usr/bin/systemctl restart nginx
|
||||
ada ALL=(root) /usr/bin/systemctl restart *
|
||||
```
|
||||
|
||||
Первое разрешает перезапустить только `nginx`. Второе намного шире и может разрешить перезапуск произвольного юнита.
|
||||
|
||||
Правило без аргументов в современных версиях `sudo` может разрешать команду с любыми аргументами. Для явного запрета аргументов используется пустая строка:
|
||||
|
||||
```sudoers
|
||||
ada ALL=(root) /usr/bin/id ""
|
||||
```
|
||||
|
||||
Поведение сопоставления аргументов и специальных символов зависит от версии `sudo`. После изменения правило нужно проверять на целевой системе.
|
||||
|
||||
## Псевдонимы
|
||||
|
||||
Псевдонимы уменьшают повторение в больших конфигурациях.
|
||||
|
||||
### User_Alias
|
||||
|
||||
```sudoers
|
||||
User_Alias OPERATORS = ada, alice, %ops
|
||||
```
|
||||
|
||||
### Runas_Alias
|
||||
|
||||
```sudoers
|
||||
Runas_Alias SERVICE_USERS = nginx, postgres
|
||||
```
|
||||
|
||||
### Host_Alias
|
||||
|
||||
```sudoers
|
||||
Host_Alias WEB_SERVERS = web01, web02
|
||||
```
|
||||
|
||||
### Cmnd_Alias
|
||||
|
||||
```sudoers
|
||||
Cmnd_Alias NGINX_CONTROL = \
|
||||
/usr/bin/systemctl status nginx, \
|
||||
/usr/bin/systemctl reload nginx, \
|
||||
/usr/bin/systemctl restart nginx
|
||||
|
||||
%ops WEB_SERVERS=(root) NGINX_CONTROL
|
||||
```
|
||||
|
||||
Имена псевдонимов принято писать в верхнем регистре.
|
||||
|
||||
## Теги
|
||||
|
||||
### PASSWD и NOPASSWD
|
||||
|
||||
По умолчанию `sudo` запрашивает пароль вызывающего пользователя:
|
||||
|
||||
```sudoers
|
||||
ada ALL=(root) /usr/bin/systemctl restart nginx
|
||||
```
|
||||
|
||||
Без пароля:
|
||||
|
||||
```sudoers
|
||||
ada ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx
|
||||
```
|
||||
|
||||
`NOPASSWD` удобно для автоматизации, но увеличивает последствия компрометации учётной записи. Его следует ограничивать минимальным набором команд.
|
||||
|
||||
Теги действуют на следующие команды в списке, пока не будут переопределены:
|
||||
|
||||
```sudoers
|
||||
ada ALL=(root) NOPASSWD: /usr/bin/systemctl status nginx, \
|
||||
PASSWD: /usr/bin/systemctl restart nginx
|
||||
```
|
||||
|
||||
### NOEXEC
|
||||
|
||||
```sudoers
|
||||
ada ALL=(root) NOEXEC: /usr/bin/less /var/log/nginx/error.log
|
||||
```
|
||||
|
||||
`NOEXEC` пытается запретить программе запускать другие процессы. Это дополнительная мера, а не надёжная граница безопасности: поддержка зависит от платформы и типа бинарника.
|
||||
|
||||
### SETENV
|
||||
|
||||
```sudoers
|
||||
ada ALL=(root) SETENV: /usr/local/sbin/deploy
|
||||
```
|
||||
|
||||
`SETENV` позволяет передавать переменные окружения с меньшим числом ограничений. Это опасно для команд, поведение которых зависит от `PATH`, загрузчиков библиотек, интерпретаторов и конфигурационных переменных.
|
||||
|
||||
## Defaults
|
||||
|
||||
Директивы `Defaults` управляют поведением `sudo`.
|
||||
|
||||
```sudoers
|
||||
Defaults env_reset
|
||||
Defaults use_pty
|
||||
Defaults timestamp_timeout=5
|
||||
Defaults passwd_tries=3
|
||||
```
|
||||
|
||||
Часто используемые параметры:
|
||||
|
||||
| Параметр | Назначение |
|
||||
|---|---|
|
||||
| `env_reset` | оставляет ограниченный набор переменных окружения |
|
||||
| `secure_path` | задаёт доверенный `PATH` для команд через `sudo` |
|
||||
| `use_pty` | запускает команду в псевдотерминале |
|
||||
| `timestamp_timeout` | срок действия успешной аутентификации в минутах |
|
||||
| `passwd_tries` | число попыток ввода пароля |
|
||||
| `log_input`, `log_output` | запись ввода и вывода поддерживаемых сессий |
|
||||
|
||||
Пример `secure_path`:
|
||||
|
||||
```sudoers
|
||||
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/bin"
|
||||
```
|
||||
|
||||
Настройки можно ограничить пользователем, группой, хостом или командой:
|
||||
|
||||
```sudoers
|
||||
Defaults:ada timestamp_timeout=0
|
||||
Defaults:%ops use_pty
|
||||
Defaults!/usr/bin/systemctl !log_output
|
||||
```
|
||||
|
||||
`timestamp_timeout=0` требует пароль для каждого отдельного запуска. Отрицательное значение создаёт сессию без ограничения по времени и обычно нежелательно.
|
||||
|
||||
## Запуск от имени другого пользователя
|
||||
|
||||
```sudoers
|
||||
ada ALL=(postgres) /usr/bin/psql
|
||||
```
|
||||
|
||||
Использование:
|
||||
|
||||
```bash
|
||||
sudo -u postgres /usr/bin/psql
|
||||
```
|
||||
|
||||
С указанием группы:
|
||||
|
||||
```sudoers
|
||||
ada ALL=(deploy:deploy) /usr/local/bin/release
|
||||
```
|
||||
|
||||
## Запреты
|
||||
|
||||
Команду можно исключить через `!`:
|
||||
|
||||
```sudoers
|
||||
ada ALL=(root) ALL, !/usr/bin/su, !/usr/bin/bash
|
||||
```
|
||||
|
||||
Такой список не создаёт безопасное ограничение полного административного доступа. Пользователь с разрешением `ALL` часто может получить оболочку другим способом: через редактор, интерпретатор, отладчик, файловый менеджер, загрузку модуля, изменение исполняемого файла или сервисной конфигурации.
|
||||
|
||||
Отрицания полезны для уточнения узкого списка, но не должны использоваться как sandbox.
|
||||
|
||||
## Опасные категории команд
|
||||
|
||||
Нельзя считать ограниченными команды, которые позволяют:
|
||||
|
||||
- запустить оболочку или произвольный процесс;
|
||||
- выполнять код или загружать модули;
|
||||
- редактировать произвольные файлы;
|
||||
- менять владельца, права или ACL;
|
||||
- записывать в исполняемые файлы и каталоги из `PATH`;
|
||||
- менять unit-файлы, cron-задачи, PAM, загрузчик или конфигурацию сервисов;
|
||||
- читать секреты и закрытые ключи;
|
||||
- управлять контейнерами или виртуальными машинами с доступом к хосту.
|
||||
|
||||
Примеры команд, которые часто позволяют выйти к root:
|
||||
|
||||
```text
|
||||
sh, bash, zsh, python, ruby, perl, vim, nvim, less, find,
|
||||
tar, rsync, awk, sed, env, systemctl, docker, podman
|
||||
```
|
||||
|
||||
Риск зависит от разрешённых аргументов, файловой системы и окружения. Само имя команды недостаточно для оценки безопасности.
|
||||
|
||||
## Безопасная автоматизация
|
||||
|
||||
Предпочтительный подход — разрешить один root-owned wrapper с фиксированным поведением:
|
||||
|
||||
```sudoers
|
||||
deploy ALL=(root) NOPASSWD: /usr/local/sbin/restart-myapp
|
||||
```
|
||||
|
||||
Требования к wrapper:
|
||||
|
||||
- файл и родительские каталоги не доступны на запись вызывающему пользователю;
|
||||
- используются абсолютные пути;
|
||||
- входные параметры проверяются по allowlist;
|
||||
- не выполняются строки через shell без необходимости;
|
||||
- окружение очищается или задаётся явно;
|
||||
- временные файлы создаются безопасно;
|
||||
- логируются значимые действия.
|
||||
|
||||
Если пользователь может изменить разрешённый скрипт, библиотеку, конфигурацию или исполняемый файл, правило фактически разрешает выполнение произвольного кода с повышенными правами.
|
||||
|
||||
## Проверка правил
|
||||
|
||||
Показать доступные текущему пользователю команды:
|
||||
|
||||
```bash
|
||||
sudo -l
|
||||
```
|
||||
|
||||
Проверить другого пользователя от root:
|
||||
|
||||
```bash
|
||||
sudo -l -U ada
|
||||
```
|
||||
|
||||
Сбросить сохранённую аутентификацию:
|
||||
|
||||
```bash
|
||||
sudo -k
|
||||
```
|
||||
|
||||
Удалить timestamp полностью:
|
||||
|
||||
```bash
|
||||
sudo -K
|
||||
```
|
||||
|
||||
Проверить конфигурацию:
|
||||
|
||||
```bash
|
||||
sudo visudo -c
|
||||
```
|
||||
|
||||
Проверять нужно не только синтаксис, но и фактический результат `sudo -l`: правила могут объединяться, а более широкое разрешение в другом файле отменяет ожидаемое ограничение.
|
||||
|
||||
## Приоритет и объединение правил
|
||||
|
||||
`sudoers` не работает как обычный firewall с простым правилом «первое совпадение победило». Для пользователя могут совпасть несколько записей; разрешения и теги вычисляются по правилам sudoers и могут объединяться.
|
||||
|
||||
Практические следствия:
|
||||
|
||||
- узкое правило не отменяет широкое разрешение из другого файла;
|
||||
- порядок важен для некоторых тегов и параметров;
|
||||
- членство пользователя в нескольких административных группах нужно учитывать;
|
||||
- проверять итог следует через `visudo -c` и `sudo -l`.
|
||||
|
||||
## Минимальный пример
|
||||
|
||||
Задача: разрешить группе `webops` проверять и перезапускать только `nginx`.
|
||||
|
||||
```sudoers
|
||||
Cmnd_Alias NGINX_READ = \
|
||||
/usr/bin/systemctl status nginx
|
||||
|
||||
Cmnd_Alias NGINX_WRITE = \
|
||||
/usr/bin/systemctl reload nginx, \
|
||||
/usr/bin/systemctl restart nginx
|
||||
|
||||
%webops ALL=(root) NGINX_READ
|
||||
%webops ALL=(root) PASSWD: NGINX_WRITE
|
||||
```
|
||||
|
||||
Проверка:
|
||||
|
||||
```bash
|
||||
sudo visudo -c
|
||||
sudo -l
|
||||
sudo /usr/bin/systemctl status nginx
|
||||
```
|
||||
|
||||
## Чек-лист
|
||||
|
||||
- Правило хранится в отдельном файле `/etc/sudoers.d/`.
|
||||
- Файл проверен через `visudo`.
|
||||
- Использованы абсолютные пути.
|
||||
- Разрешены конкретные команды и аргументы.
|
||||
- Нет лишнего `ALL`.
|
||||
- `NOPASSWD` используется только при необходимости.
|
||||
- Пользователь не может изменить разрешённую команду или её зависимости.
|
||||
- Не разрешены оболочки, редакторы и интерпретаторы без осознанной причины.
|
||||
- Учтены переменные окружения и `secure_path`.
|
||||
- Итоговые права проверены через `sudo -l`.
|
||||
- Есть журналирование и способ аварийного получения root-доступа.
|
||||
|
||||
## Связанные заметки
|
||||
|
||||
- [[Super-user]]
|
||||
- [[File Permissions]]
|
||||
- [[Linux - MOC]]
|
||||
@@ -0,0 +1,150 @@
|
||||
---
|
||||
created: 2026-06-08
|
||||
updated: 2026-06-09
|
||||
---
|
||||
МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ
|
||||
|
||||
Федеральное государственное автономное образовательное учреждение высшего образования
|
||||
|
||||
НАЦИОНАЛЬНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ ЯДЕРНЫЙ УНИВЕРСИТЕТ «МИФИ»
|
||||
|
||||
<br>
|
||||
|
||||
**Учебно-исследовательская работа на тему:**
|
||||
«Аналитический обзор решений на рынке по системам контроля посещаемости в ВУЗе, сравнение с собственной разработкой и обоснование выбора технологического стека»
|
||||
|
||||
**Выполнил студент:** [ФИО студента]
|
||||
**Группа:** [Номер группы]
|
||||
|
||||
**Проверил:**
|
||||
[ФИО преподавателя]
|
||||
**Оценка:** ___________________
|
||||
**Дата:** _____________________
|
||||
**Подпись:** __________________
|
||||
|
||||
Москва, 2026
|
||||
|
||||
---
|
||||
|
||||
## Оглавление
|
||||
1. Введение
|
||||
2. Глава 1. Анализ существующих решений и систем контроля посещаемости
|
||||
1.1. Обзор российских систем
|
||||
1.2. Обзор зарубежных систем
|
||||
1.3. Сравнительный анализ аналогов
|
||||
3. Глава 2. Обоснование собственной разработки и выбора технологического стека
|
||||
2.1. Концепция системы Attendance MEPhI
|
||||
2.2. Выбор веб-фреймворка и архитектуры приложения
|
||||
2.3. Фронтенд-архитектура: отказ от SPA в пользу Hotwire
|
||||
2.4. Выбор СУБД и организация бизнес-логики
|
||||
2.5. Асинхронная обработка и надежная синхронизация (Outbox Pattern)
|
||||
2.6. Интеграция с корпоративной системой аутентификации (CAS)
|
||||
4. Заключение
|
||||
5. Список использованной литературы
|
||||
|
||||
---
|
||||
|
||||
## Введение
|
||||
|
||||
Цифровая трансформация образовательного процесса в современных университетах требует внедрения надежных и прозрачных инструментов контроля успеваемости и присутствия обучающихся. В условиях крупного вуза, такого как НИЯУ МИФИ, традиционные бумажные журналы или несистематизированные электронные таблицы становятся неэффективными, повышая риск потери данных и усложняя сбор аналитики для деканатов и администрации.
|
||||
|
||||
В связи с этим возникает потребность в автоматизированной системе учета посещаемости, способной интегрироваться с внутренними реестрами вуза и обеспечивать удобный интерфейс как для преподавателей, так и для студентов. Готового аналога, закрывающего все требования МИФИ, на рынке нет: зарубежные универсальные инструменты фиксируют посещаемость лишь как побочный продукт онлайн-встреч и не интегрируются с CAS МИФИ, а российские решения ориентированы на школу или корпоративное обучение, не давая студенческой QR-самоотметки и Outbox-синхронизации[cite: 1]. Это прямо обосновывает разработку собственной системы[cite: 1].
|
||||
|
||||
**Актуальность работы** обусловлена отсутствием на рынке универсального on-premise решения, которое сочетало бы механизм быстрой самостоятельной отметки студентов на очных занятиях, интеграцию с корпоративной системой авторизации и гарантированную асинхронную доставку данных в академические реестры университета[cite: 1].
|
||||
|
||||
**Целью работы** является проведение аналитического обзора существующих рыночных решений в сфере контроля посещаемости, их сравнение с разрабатываемой системой «Attendance MEPhI» и обоснование выбранного технологического стека[cite: 3].
|
||||
|
||||
**Задачи исследования:**
|
||||
1. Провести обзор российских и зарубежных аналогов систем контроля посещаемости.
|
||||
2. Сформировать сравнительную матрицу функциональных возможностей.
|
||||
3. Обосновать необходимость разработки собственного программного решения.
|
||||
4. Выполнить обоснование выбранной архитектуры, фреймворка, СУБД и сопутствующих технологий[cite: 1, 3].
|
||||
|
||||
---
|
||||
|
||||
## Глава 1. Анализ существующих решений и систем контроля посещаемости
|
||||
|
||||
Ни один массовый продукт не сочетает одновременно четыре ключевых свойства, необходимых корпоративному вузу: QR-самоотметку студента на очном занятии, интеграцию с корпоративным SSO (CAS МИФИ), гарантированную синхронизацию с внутренними реестрами и полную ролевую модель[cite: 1].
|
||||
|
||||
### 1.1. Обзор российских систем
|
||||
|
||||
**1. БАРС.Образование — Электронная школа**
|
||||
Одна из самых распространенных в РФ платформ электронного дневника[cite: 1]. Модуль ориентирован на фиксацию посещаемости исключительно путем ручной отметки учителем[cite: 1]. Основным архитектурным ограничением является использование школьной модели данных («школа — класс — родитель»), которая не масштабируется на сложную вузовскую структуру («учебный поток — группа — подгруппа»)[cite: 1]. Кроме того, интеграция реализуется через СМЭВ/Госуслуги, что затрудняет использование корпоративного CAS вуза[cite: 1].
|
||||
|
||||
**2. iSpring Learn (iSpring LMS)**
|
||||
Известная российская корпоративная LMS, доступная в облаке вендора и on-premise[cite: 1]. Включает отчет по посещаемости очных тренингов, где преподаватель отмечает участников вручную[cite: 1]. Система предназначена для корпоративного обучения персонала, не поддерживает ролевую модель учебного потока вуза и QR-самоотметку на занятии, а также не имеет Outbox-синхронизации с академическими реестрами[cite: 1].
|
||||
|
||||
**3. 1С:Университет**
|
||||
Отраслевое решение на платформе «1С:Предприятие 8.3» для комплексного управления вузом[cite: 1]. Посещаемость вводится вручную через АРМ[cite: 1]. В решении отсутствует студенческий QR-self-service и нативное веб-самообслуживание[cite: 1]. Интеграция требует механизмов обмена 1С, а не современных REST/CAS[cite: 1].
|
||||
|
||||
**4. Система «Учет посещаемости студентов» ВВГУ**
|
||||
Собственная разработка Департамента цифрового развития ВВГУ[cite: 1]. Поддерживает QR-отметку и имеет частичную ролевую модель (роль куратора)[cite: 1]. Главный недостаток — это закрытая внутренняя система одного вуза без публичного API и Outbox-синхронизации, не интегрируемая с внешними CAS МИФИ[cite: 1].
|
||||
|
||||
### 1.2. Обзор зарубежных систем
|
||||
|
||||
**1. Microsoft Teams и Google Classroom/Meet**
|
||||
Обе платформы фиксируют присутствие на видеовстрече (время входа/выхода), а не на очном занятии[cite: 1]. Нативной отметки очных занятий по QR нет, интеграция с внутренними реестрами и вузовская ролевая модель отсутствуют[cite: 1].
|
||||
|
||||
**2. Brightspace (D2L) — Attendance tool**
|
||||
Базовый инструмент представляет собой электронный реестр, где отметку ставит преподаватель или ассистент вручную[cite: 1]. Мобильная самоотметка и QR реализуются только через сторонние интеграции и платные плагины (Qwickly Attendance), что требует внедрения всей LMS Brightspace[cite: 1].
|
||||
|
||||
**3. Специализированные QR/чек-ин SaaS решения (OneTap, AccuClass)**
|
||||
Эти сервисы предоставляют функционал сканирования QR-кодов, однако ориентированы на мероприятия или школы[cite: 1]. Данные хранятся в облаке вендора (vendor lock-in), нет вузовской ролевой модели и Outbox-синхронизации[cite: 1].
|
||||
|
||||
### 1.3. Сравнительный анализ аналогов
|
||||
|
||||
**Таблица 1 — Сравнительный анализ систем учета посещаемости[cite: 1]**
|
||||
|
||||
| Система | QR-self-service | SSO/CAS-интеграция | Outbox-синхронизация | Вузовская ролевая модель | On-premise |
|
||||
| :--- | :---: | :---: | :---: | :---: | :---: |
|
||||
| ВВГУ "Учет посещаемости" | ✔ | ✘ | ✘ | Частично | ✔ |
|
||||
| БАРС.Образование | ✘ | ✘ (Госуслуги) | ✘ | Школьная | Зависит |
|
||||
| iSpring Learn | ✘ | ✔ (SSO) | ✘ | Корпоративная | ✔ |
|
||||
| 1С:Университет | ✘ | ✘ | ✘ (обмен 1С) | ✔ | ✔ |
|
||||
| MS Teams | ✘ | ✔ (Azure AD) | ✘ | ✘ | ✘ |
|
||||
| Google Classroom/Meet | ✘ | ✔ (Google) | ✘ | ✘ | ✘ |
|
||||
| Brightspace | Через плагины | ✔ | ✘ | LMS | ✔ |
|
||||
| QR-SaaS (OneTap и др.) | ✔ | Частично | ✘ | ✘ | ✘ |
|
||||
| **Attendance MEPhI (Проект)** | **✔** | **✔ (CAS МИФИ)**| **✔** | **✔ (полная)**| **✔** |
|
||||
|
||||
**Вывод по Главе 1:** Ниша систем для QR-самоотметки очной посещаемости с интеграцией CAS-SSO вуза, Outbox-синхронизацией с реестрами и полной ролевой моделью не закрыта существующими продуктами, что служит обоснованием разработки[cite: 1].
|
||||
|
||||
---
|
||||
|
||||
## Глава 2. Обоснование собственной разработки и выбора технологического стека
|
||||
|
||||
### 2.1. Концепция системы Attendance MEPhI
|
||||
**Attendance MEPhI** — это веб-приложение для отслеживания посещаемости занятий студентами НИЯУ МИФИ[cite: 3]. Система автоматизирует процесс фиксации присутствия студентов на лекциях и семинарах, предоставляя интерфейсы для студентов, преподавателей (тьюторов), модераторов и администраторов[cite: 3]. Проект построен на классическом паттерне MVC с использованием современных расширений Rails[cite: 3].
|
||||
|
||||
### 2.2. Выбор веб-фреймворка и архитектуры приложения
|
||||
Стек Rails 7 обоснован архитектурно[cite: 1]. Rails реализует строгий паттерн MVC, принципы «Convention over Configuration» и DRY, что ускоряет разработку CRUD-приложений[cite: 1]. Хотя Ruby исторически уступает по "сырой" производительности оптимизированным PHP/Python, это компенсируется фоновой обработкой, кэшированием и горизонтальным масштабированием[cite: 1].
|
||||
|
||||
### 2.3. Фронтенд-архитектура: отказ от SPA в пользу Hotwire
|
||||
Server-rendered HTML через Hotwire (Turbo + Stimulus) снимает необходимость в отдельном SPA[cite: 1]. Он отправляет фрагменты HTML вместо JSON, обеспечивая отзывчивый интерфейс без тяжелого клиентского JS[cite: 1, 3]. Для стилизации используется Tailwind CSS, utility-first подход которого дает существенно меньший production-бандл за счет JIT-purge и не тянет лишние JS-компоненты[cite: 1, 3].
|
||||
|
||||
### 2.4. Выбор СУБД и организация бизнес-логики
|
||||
В качестве хранилища данных выбрана PostgreSQL[cite: 1, 3]. Это ACID-совместимая СУБД с MVCC, расширенным набором типов и ролевой моделью доступа, предпочтительная для транзакционных приложений с большими объемами данных[cite: 1].
|
||||
Бизнес-логика (импорт данных, обработка посещений) изолируется в Service Objects (`app/services`), что соответствует принципу единой ответственности и упрощает тестирование[cite: 1, 3]. Ролевая модель поддерживается через пространства имен контроллеров (Namespaced Controllers: `Admin`, `Student` и т.д.), что изолирует ролевые интерфейсы и упрощает авторизацию[cite: 1, 3].
|
||||
|
||||
### 2.5. Асинхронная обработка и надежная синхронизация (Outbox Pattern)
|
||||
Для гарантированной доставки данных во внешние реестры МИФИ используется Outbox Pattern (таблица `update_attendance_students_outbox`)[cite: 1, 3]. Событие записывается в outbox-таблицу в той же транзакции, что и бизнес-данные, решая проблему "dual write"[cite: 1]. Отдельный процесс (Sidekiq) асинхронно публикует его наружу, обеспечивая доставку at-least-once[cite: 1]. Sidekiq использует Redis как in-memory хранилище очередей и эффективен по памяти благодаря многопоточной модели[cite: 1, 3].
|
||||
|
||||
### 2.6. Интеграция с корпоративной системой аутентификации (CAS)
|
||||
Система интегрируется с CAS (Central Authentication Service) МИФИ для Single Sign-On[cite: 3]. CAS — это протокол, при котором приложение никогда не видит пароль пользователя: аутентификация выполняется на доверенном сервере, выдающем service ticket[cite: 1]. Это снижает риск фишинга и избавляет от необходимости вести локальную базу паролей[cite: 1].
|
||||
|
||||
---
|
||||
|
||||
## Заключение
|
||||
|
||||
Анализ показал, что ни одна из массовых российских или зарубежных систем не удовлетворяет одновременно требованиям QR-самоотметки, интеграции с CAS МИФИ, наличию механизмов надежной Outbox-репликации данных и поддержке специфической ролевой модели вуза. Выбранный технологический стек (Ruby on Rails 7, PostgreSQL, Hotwire, Sidekiq) обеспечивает высокую скорость разработки, SPA-ощущение без перегрузки клиентской части JavaScript-кодом и промышленную надежность доставки данных во внешние реестры. Разработка системы «Attendance MEPhI» обоснована и технически реализуема.
|
||||
|
||||
---
|
||||
|
||||
## Список использованной литературы
|
||||
|
||||
1. Nuhi A., Memeti A., Imeri F., Çiço B. Smart Attendance System using QR Code // 2020 9th Mediterranean Conference on Embedded Computing (MECO), Budva, Montenegro, 2020, pp. 1–4. DOI: 10.1109/MECO49872.2020.9134225.
|
||||
2. Глуховский К.С., Пирожков Р.В., Цвелик Е.А. Электронный журнал как элемент цифровой трансформации вуза // Инженерный вестник Дона. 2021. №5.
|
||||
3. Salunke S.V., Ouda A. A Performance Benchmark for the PostgreSQL and MySQL Databases // Future Internet (MDPI). 2024. Vol. 16(10). Art. 382. DOI: 10.3390/fi16100382.
|
||||
4. Даньшин К.А. Сравнительный анализ современных веб-фреймворков для разработки приложений // CyberLeninka, 2019.
|
||||
5. Chris Richardson. Microservices Patterns: With examples in Java. Manning Publications. (Pattern: Transactional outbox).
|
||||
|
||||
@@ -0,0 +1,296 @@
|
||||
---
|
||||
status: seed
|
||||
type: concept
|
||||
tags: []
|
||||
created: 2025-12-17
|
||||
updated: 2026-06-16
|
||||
aliases: []
|
||||
---
|
||||
|
||||
# Безопасность инфраструктуры. ZTNA, SASE, DiD
|
||||
|
||||
## Zero trust network access
|
||||
|
||||
**ZTNA** - Zero Trust Network Access - давно известный подход по обеспечению безопасности, основанный на концепции нулевого доверия.
|
||||
ZTNA является реинкарнацией старого подхода ZT, своеобразной рыночной упаковкой.
|
||||
|
||||
> [!note] Бесспорные факторы популярности подхода ZTNA:
|
||||
> - Возрастающая популярность облачных технологий.
|
||||
> - Массовый переход на удаленный формат работы (COVID-19).
|
||||
|
||||
### Нулевое доверие
|
||||
Концепция **“нулевого доверия”** предполагает, что решение о предоставлении или непредоставлении доступа к ресурсу принимается каждый раз в момент самого обращения.
|
||||
При сценарии “нулевого доверия” нет никаких доверенных подключений, поэтому моделирование угроз развивается изначально по худшему из возможных сценариев.
|
||||
<u>*ZTNA* не отменяет существующую модель угроз, а изменяет ее</u>.
|
||||
|
||||
*Распространенные сценарии применения:*
|
||||
1) Удаленный доступ с личных устройств
|
||||
2) Гостевые и временные доступы
|
||||
3) Контроль интернета вещей (IoT)
|
||||
|
||||
*Источники информации (контексты) для принятия решения о предоставлении или непредоставлении доступа в ZTNA:*
|
||||
- Сетевой контекст (тип подключения, геолокация).
|
||||
- Контекст устройства (модель, ОС, обновления, локализация).
|
||||
- Пользовательский контекст (идентификация).
|
||||
- Ролевой контекст (авторизация).
|
||||
- Контекст безопасности (компрометация данных).
|
||||
- .. и многое другие
|
||||
|
||||
*Внедрение ZTNA требует достаточных предварительных действий:*
|
||||
1. Определить сценарии применения, контексты, оценку рисков.
|
||||
2. Определить **роли** пользователей и необходимые им **ресурсы**.
|
||||
3. Подобрать правильные, подходящие в вашем случае **инструменты**.
|
||||
4. Поддержка вашего рабочего окружения.
|
||||
5. Готовность внедрять ZTNA последовательно и поэтапно.
|
||||
|
||||
*Процесс внедрения ZTNA:*
|
||||
1) Отказ от статических IP-адресов
|
||||
2) Отказ от White-Lists
|
||||
3) Идентифицировать каждого пользователя и каждый пользовательский запрос
|
||||
4) Сегментировать имеющиеся ресурсы и доступы к ним
|
||||
5) Отмена разделения пользователей на своих/чужих (гостей, подрядчиков, временных пользователей)
|
||||
6) Внедрить многофакторную аутентификацию везде, где это применимо, но поэтапно.
|
||||
|
||||
**ZTNA** — это маркетинговая реинкарнация давно существующей модели "нулевого доверия".
|
||||
|
||||
Концептуально ZTNA не привязана к конкретной технологии или вендору.
|
||||
|
||||
Начать внедрять ZTNA можно и нужно поэтапно, начать достаточно с малого. К примеру, двух-факторная аутентификация пользователей поможет защититься более, чем от 70% потенциальных взломов*.
|
||||
|
||||
\**по версии журнала Anti-malware.*
|
||||
|
||||
## Secure access service edge
|
||||
|
||||
**SASE** — Secure Access Service Edge.
|
||||
|
||||
Достаточно новый подход, находящийся на стыке решения двух важных задач — обеспечения информационной безопасности и выбора оптимального способа подключения к ресурсам.
|
||||
*SASE* является комплексным подходом и предоставляется множеством вендоров.
|
||||
|
||||
Концепция *SASE* разработана исследовательской компанией Gartner в 2019 году.
|
||||
|
||||
Главная цель *SASE* сформулирована следующим образом — сервис безопасного и оптимального доступа к IT-ресурсам (симбиоз выбора стабильного канала связи и обеспечения информационной безопасности ресурсов).
|
||||
|
||||
*Предпосылки возникновения SASE во многом схожи с ZTNA:*
|
||||
- Размытие периметра инфраструктуры.
|
||||
- Развитие облачных технологий.
|
||||
- Массовый переход на удаленную работу.
|
||||
- Использование личных устройств.
|
||||
- COVID-19.
|
||||
|
||||
*Gartner составило список обязательных технологий, которые должны быть реализованы в SASE:*
|
||||
|
||||
- **ZTNA (Zero Trust Network Access)** - модель сетевого доступа к ресурсам на основе политики “нулевого доверия”.
|
||||
- **SD-WAN (Software-Defined Wide Area Network)** — динамический подбор наилучшего способа подключения в зависимости от производительности.
|
||||
- **SWG (Secure Web Gateway)** — безопасный сетевой шлюз.
|
||||
- **CASB (Cloud Access Security Broker)** — брокер безопасного доступа к облачным ресурсам.
|
||||
- **FWaaS (Firewall-as-a-Service)** — сервис облачного сетевого экрана.
|
||||
|
||||
*Помимо обязательных компонентов, вендоры SASE могут реализовывать дополнительные, необязательные компоненты:*
|
||||
|
||||
- **DLP** (data leak prevention) — предотвращение утечек.
|
||||
- **QoS** (quality of service) — приоритизация трафика.
|
||||
- **NGFW** (web application firewall).
|
||||
- **VPN** (virtual private network) — виртуальная частная сеть.
|
||||
- **UEBA** (user entity behaviour analysis) — поведенческий анализ.
|
||||
- **Антифрод инструменты** — оценка вероятности финансового мошенничества.
|
||||
- **Инструменты обфускации** — изменение исходного кода в целях затруднения анализа при сохранении функциональности.
|
||||
- **Инструменты защиты DNS / WiFi от подмена и взлома.**
|
||||
- и многие другие
|
||||
|
||||
Вендоры SASE-решений, реализовавшие основные компоненты, могут получить *сертификацию Gartner*.
|
||||
Отечественных вендоров SASE-решений не существует до сих пор. (
|
||||
|
||||
*Прогноз популярности SASE по версии аналитиков Gartner:*
|
||||
|
||||
- **SASE** выйдет на плато *продуктивности* в течение 2024-2029.
|
||||
- К 2024 году как минимум 40% предприятий будут иметь четкие стратегии по внедрению SASE (по сравнению с 1% на начало 2019 года).
|
||||
- Вендоры будут переоценивать свои возможности по предоставлению услуг SASE.
|
||||
|
||||
Концепция SASE сформулирована компанией Gartner, она состоит из конвергентного решения по оптимизации доступа и информационной безопасности ИТ-ресурсов.
|
||||
|
||||
Реализовав список обязательных компонентов, вендор может претендовать на сертификацию своего решения у *Gartner*, реализовав как можно больше дополнительных компонентов эффективно конкурировать с другими вендорами.
|
||||
|
||||
## Defense-in-depth
|
||||
|
||||
**DiD** — Defense-in-depth.
|
||||
|
||||
DiD является концепцией глубокой защиты, в его основу заложены несколько степеней защиты, методы которых не пересекаются.
|
||||
Подход призван задержать и усложнить продвижение и действия злоумышленника.
|
||||
Концепция глубокоэшелонированной защиты заимствована из общей военной стратегии и разработана агентством национальной безопасности США.
|
||||
|
||||
*DiD делит организацию защиты инфраструктуры на три контролируемые части:*
|
||||
|
||||
- Физическая средства защиты.
|
||||
- Технические средства защиты.
|
||||
- Административные средства защиты.
|
||||
|
||||
*К физическим средствам защиты можно отнести:*
|
||||
- Охранники и охранные системы.
|
||||
- Системы контроля и управления доступом (СКУД)
|
||||
- Видеонаблюдение (CCTV)
|
||||
- Сигнализационные системы.
|
||||
- Запертые двери, шкафы, замки, сейфы.
|
||||
|
||||
*К техническим средствам защиты можно отнести:*
|
||||
- Контроль сетевого доступа.
|
||||
- Межсетевые экраны.
|
||||
- Антивирусная защита.
|
||||
- Прокси-серверы.
|
||||
- Системы аутентификации и авторизации.
|
||||
|
||||
*К административным средствам защиты можно отнести:*
|
||||
- Регулирование и управления самих средств защиты (документы и нормативка?)
|
||||
- Обработка сенсетивной информации (пароли, токены и т.д.)
|
||||
- Ведение списков разрешенных и запрещенных ПО.
|
||||
- Политики взаимодействия с гостевыми допусками, внешними ресурсами и организациями.
|
||||
|
||||
*С чего начать внедрение DiD:*
|
||||
- Включение защиты от привилегированного доступа.
|
||||
- Использование средств *Observability*.
|
||||
- Развитие культуры DevSecOps.
|
||||
- Внедрение многофакторной аутентификации.
|
||||
- Использование моделей “*нулевого доверия*”.
|
||||
- Внедрение *SASE*-решений.
|
||||
- Использование безопасных инструментов разработки и практик (секьюрные заголовки в запросах, защита куки, централизованное управление секретами и паролями).
|
||||
- Своевременный аудит безопасности.
|
||||
- Использование сервисных сеток и инфраструктурных сканеров.
|
||||
|
||||
> [!note] DiD
|
||||
> - Концепция Defense-in-depth основывается на военных принципах глубокоэшелонированной защиты.
|
||||
> - DiD разделяет защиту на три независимых контура — физическую, техническую и административную.
|
||||
> - Три степени защиты призваны замедлить продвижение потенциального злоумышленника.
|
||||
|
||||
## Сканеры инфраструктуры
|
||||
|
||||
Для выполнения сканирования подконтрольной инфраструктуры на предмет уязвимостей используются **разнообразные** **инфраструктурные сканеры**, позволяющие обнаруживать:
|
||||
- Наборы слабых шифров.
|
||||
- Небезопасные конфигурации и протоколы.
|
||||
- Стандартные и дефолтные пароли, порты.
|
||||
- Не обновленные приложения и библиотеки.
|
||||
- Просроченные и скомпрометированные сертификаты.
|
||||
|
||||
Примером проверенных инфраструктурных сканеров может быть opensource-решение “*Openvas*” от сообщества “Greenbone”.
|
||||
|
||||
![[Pasted image 20260616205715.png|737]]
|
||||
|
||||
## Часть 6. Рефлексия и практическая работа
|
||||
|
||||
### Проверка достижения целей урока
|
||||
|
||||
1. Познакомились с основными принципами обеспечения безопасности стека приложений и инфраструктуры.
|
||||
2. Узнали, как связаны ZTNA, SASE и DiD друг с другом.
|
||||
3. Познакомились с инфраструктурными сканерами.
|
||||
4. Закрепили на практике установку и работу с инструментом сканирования инфраструктуры.
|
||||
5. На примере рассмотрели закрытие потенциальной уязвимости в Nginx.
|
||||
|
||||
### Практическая работа
|
||||
|
||||
1. Самостоятельно проинсталлировать инфраструктурный сканер.
|
||||
2. Самостоятельно выполнить сканирование инфраструктуры.
|
||||
3. Изучить список литературы.
|
||||
|
||||
#### Самостоятельно происталировать инфраструктурный сканер
|
||||
|
||||
- Ознакомьтесь с методическим материалом по развертыванию и использованию инфраструктурного сканера.
|
||||
- Выполните действия, описанные в методическом материале.
|
||||
- Установите инфраструктурный сканер.
|
||||
|
||||
#### Самостоятельно выполнить сканирование инфраструктуры
|
||||
|
||||
- Используя установленный в задании #1 инфраструктурный сканер, выполните сканирование подконтрольной инфраструктуры.
|
||||
- Убедитесь, что сканирование успешно завершено и самостоятельно изучите отчет о сканировании.
|
||||
|
||||
#### Изучить список литературы
|
||||
|
||||
Самостоятельно изучите приложенный список дополнительной литературы.
|
||||
|
||||
### Методический материал
|
||||
|
||||
Развёртывание и использование инфраструктурного сканера Для начала работ вам понадобится docker.
|
||||
|
||||
Подробная установка docker рассматривалась в методических материалах к предыдущим урокам.
|
||||
|
||||
Запустим контейнер с инструментом openvas, используя image пользователя mikesplain:
|
||||
|
||||
$ docker run -d -p 443:443 -p 9390:9390 --name openvas
|
||||
mikesplain/openvas
|
||||
|
||||

|
||||
|
||||
Убедимся в том, что контейнер успешно запущен:
|
||||
|
||||
$ docker ps
|
||||
|
||||

|
||||
|
||||
Контейнер запущен успешно, в браузере выполним переход в веб-интерфейс развернутого инструмента openvas:
|
||||
|
||||
_browser: https://localhost/_
|
||||
|
||||

|
||||
|
||||
Авторизуемся, используя административные учетные данные по умолчанию (admin:admin):
|
||||
|
||||

|
||||
|
||||
Переходим в раздел "Scan", выбираем в выпадающем меню поле "Tasks":
|
||||
|
||||

|
||||
|
||||
В верхнем левом углу нажимаем на кнопку со звездочкой и выбираем "New Task":
|
||||
|
||||

|
||||
|
||||
Справа от поля "Scan Target" нажимаем на кнопку со звездочкой и переходим в интерфейс указания нового target для сканирования:
|
||||
|
||||

|
||||
|
||||
Далее указываем наименование target и ip-address (в моем случае это localhost, если вы сканируете инфраструктуру, убедитесь, что обеспечен сетевой доступ по портам 22, 80, 443 для полноты возможностей сканирования):
|
||||
|
||||

|
||||
|
||||
Сохраняем изменение, нажав кнопку "Create". Убедимся, что в настройках Task подставился созданный target:
|
||||
|
||||

|
||||
|
||||
Нажимаем кнопку "Create". Созданный Task отображается в списке tasks, но еще не запущен.
|
||||
|
||||

|
||||
|
||||
Для запуска Task нажимаем кнопку начала сканирования ("Start") в разделе "Actions" нашего Task. Состояние Task изменилось на "Requested":
|
||||
|
||||

|
||||
|
||||
Через некоторое время обновим страницу и убедимся, что строка состояния нашего Task изменяется на количество процентов от выполненного сканирования:
|
||||
|
||||

|
||||
|
||||
Сканирование может занять продолжительное время.
|
||||
|
||||
Дождемся завершения сканирования, убедившись, что статус сканирования изменился на "Done":
|
||||
|
||||

|
||||
|
||||
Перейдем к результатам сканирования, нажав на статус сканирования:
|
||||
|
||||

|
||||
|
||||
В открывшемся отчете можем ознакомиться со всеми результатами сканирования, нажав на них и перейдя в подробное описание:
|
||||
|
||||

|
||||
|
||||
Подобным образом можем отработать все срабатывания, предприняв необходимые действия по их устранению. Полезно принимать в расчет рекомендации по митигации обнаруженных рисков (поле "Solution")
|
||||
|
||||
#### Список дополнительной литературы
|
||||
|
||||
[Базовое русскоязычное описание модели доступа ZTNA](https://codeby.net/threads/chto-takoye-ztna-i-zachem-on-nuzhen.84541/)
|
||||
[Обзор эволюции концепции ZTNA в мультиоблачных средах](https://securitymedia.org/info/zero-trust-arkhitektura-v-2025-godu-printsipy-kontseptsii-nulevogo-doveriya-i-ee-razvitie.html)
|
||||
[Практические рекомендации по первой фазе реализации ZTNA](https://www.kaspersky.ru/blog/zero-trust-transition-practical-advice/39484/)
|
||||
[Влияние ИИ на адаптивность и безопасность решений SASE](https://habr.com/ru/articles/914496/)
|
||||
[Кейс по уязвимости XZ Utils и практической ценности DiD](https://www.wiz.io/academy/defense-in-depth/)
|
||||
[Разбор уровней контроля (физический, технический, административный)](https://www.wallarm.com/what/defense-in-depth-concept/)
|
||||
[Современные мультислойные меры безопасности](https://www.fortinet.com/resources/cyberglossary/defense-in-depth/)
|
||||
[Концепция многоуровневой защиты, сегментация, ограничение ущерба, адаптивность защиты](https://www.isaca.org/resources/news-and-trends/isaca-now-blog/2025/beyond-the-moat-modern-defense-in-depth-strategies/)
|
||||
[Обзор новых ИБ-продуктов весны 2025](https://www.itsec.ru/articles/obzor-novyh-ib-produktov-vesny-2025-g/)
|
||||
[Разбор трех технологических волн: AI-инструменты, DevSecOps, Platform Engineering](https://habr.com/ru/companies/oleg-bunin/articles/887316/)
|
||||
|
||||
@@ -3,10 +3,10 @@ status: processing
|
||||
type: concept
|
||||
tags: []
|
||||
created: 2025-12-17
|
||||
updated: 2026-06-05
|
||||
updated: 2026-06-08
|
||||
aliases: []
|
||||
---
|
||||
|
||||
![[Pasted image 20260608084226.png]]
|
||||
# Вопросы по Вебу
|
||||
|
||||
## Теория по блокам
|
||||
@@ -0,0 +1,168 @@
|
||||
---
|
||||
created: 2026-06-08
|
||||
updated: 2026-06-08
|
||||
---
|
||||
|
||||
# УИР:
|
||||
## TL;DR
|
||||
- **Готового аналога, закрывающего все требования МИФИ, на рынке нет:** зарубежные универсальные инструменты (Microsoft Teams, Google Classroom/Meet, Brightspace) фиксируют посещаемость лишь как побочный продукт онлайн-встреч и не интегрируются с CAS МИФИ и внутренними реестрами; российские ЭЖ/LMS (БАРС.Образование, iSpring Learn, 1С:Университет) ориентированы на школу или корпоративное обучение, не дают студенческой QR-самоотметки и Outbox-синхронизации. Это прямо обосновывает разработку собственной системы.
|
||||
- **Стек Rails 7 + Hotwire + PostgreSQL + Sidekiq/Redis + Tailwind обоснован архитектурно:** server-rendered HTML через Hotwire снимает необходимость в отдельном SPA, Outbox-паттерн гарантирует надёжную (at-least-once) доставку данных во внешние реестры, Service Objects изолируют бизнес-логику, namespaced-контроллеры естественно поддерживают ролевую модель вуза.
|
||||
- **Академическая база достаточна для УИР:** по QR-посещаемости есть прямой IEEE-источник (Nuhi et al., 2020, DOI 10.1109/MECO49872.2020.9134225); по ЭЖ вуза — отраслевая статья ВИТИ НИЯУ МИФИ (Глуховский и др., 2021); по СУБД — рецензируемый бенчмарк Salunke & Ouda (Future Internet, MDPI, 2024, DOI 10.3390/fi16100382). Часть технических обоснований (Hotwire, Tailwind, Sidekiq) опирается на документацию и отраслевые источники, что следует честно помечать.
|
||||
|
||||
## Key Findings
|
||||
|
||||
### Часть 1. Аналоги
|
||||
1. Ни один массовый продукт не сочетает одновременно четыре ключевых свойства, необходимых корпоративному вузу: **(а)** QR-самоотметку студента на очном занятии, **(б)** интеграцию с корпоративным SSO (CAS МИФИ), **(в)** гарантированную (Outbox) синхронизацию с внутренними академическими реестрами, **(г)** полную ролевую модель (студент / преподаватель / куратор / деканат / администратор).
|
||||
2. Российские школьные ЭЖ (БАРС, Дневник.ру, ЭлЖур, "Сетевой город") реализуют посещаемость как **ручную отметку учителя**; модель "класс — родитель" не масштабируется на вузовскую "поток — группа — подгруппа".
|
||||
3. Зарубежные инструменты считают **присутствие на онлайн-встрече** (время входа/выхода), что не эквивалентно присутствию на очном занятии.
|
||||
|
||||
### Часть 2. Инструментарий
|
||||
Каждый компонент стека имеет рецензируемое либо авторитетное первичное обоснование, пригодное для цитирования в УИР; "слабые места" (производительность Ruby, противоречивость бенчмарков СУБД, небиблиометричность части источников) отмечены явно.
|
||||
|
||||
---
|
||||
|
||||
## Details
|
||||
|
||||
## ЧАСТЬ 1. ОБЗОР СУЩЕСТВУЮЩИХ АНАЛОГОВ
|
||||
|
||||
### Российские системы
|
||||
|
||||
**1. Система "Учёт посещаемости студентов" ВВГУ (ВГУЭС)**
|
||||
- *Описание:* собственная разработка Департамента цифрового развития ВВГУ. Преподаватель показывает QR-код, студенты сканируют его для записи о присутствии; возможна ручная отметка преподавателем или назначенным кафедрой куратором группы.
|
||||
- *Функции:* QR-отметка, ручная отметка, роль куратора.
|
||||
- *Ограничения:* закрытая внутренняя система одного вуза; нет публичного API/Outbox-синхронизации; права куратора выдаются по бумажному заявлению в Центр ИТ-обеспечения.
|
||||
- *Почему хуже для МИФИ:* не интегрируется с внешними CAS/реестрами МИФИ; ограниченная ролевая модель.
|
||||
|
||||
**2. БАРС.Образование — Электронная школа**
|
||||
- *Описание:* одна из самых распространённых в РФ платформ ЭЖ/электронного дневника; модуль "БАРС.Образование — Электронная школа" рекомендован Минкомсвязью к тиражированию в рамках мероприятия "Электронный регион"; вход — по логину/паролю Госуслуг.
|
||||
- *Функции:* электронный журнал (оценки, темы уроков, отметка посещаемости — вручную учителем), электронный дневник, планирование учебного процесса, расписание, отчётность.
|
||||
- *Ограничения:* посещаемость только ручная (нет QR/самоотметки); архитектура "школа — класс — родитель".
|
||||
- *Почему хуже для МИФИ:* школьная модель данных; интеграция реализуется через СМЭВ/Госуслуги, а не через корпоративный CAS вуза; проприетарность.
|
||||
|
||||
**3. iSpring Learn (iSpring LMS)**
|
||||
- *Описание:* российская корпоративная LMS (компания iSpring, Йошкар-Ола; платформу используют клиенты в 172 странах), доступна в облаке вендора и в установке на сервер клиента (on-premise).
|
||||
- *Функции:* курсы, тесты, вебинары (интеграция с Zoom), отчёт "ILT Attendance" по посещаемости очных тренингов/вебинаров (преподаватель отмечает участников вручную после мероприятия), SSO, открытый API, мобильное приложение.
|
||||
- *Ограничения:* посещаемость — побочная функция "мероприятий"; нет QR-самоотметки на занятии; нет модели "учебный поток вуза".
|
||||
- *Почему хуже для МИФИ:* предназначена для корпоративного обучения персонала, а не для учёта очной посещаемости студентов; нет Outbox-синхронизации с академическими реестрами.
|
||||
|
||||
**4. 1С:Университет**
|
||||
- *Описание:* отраслевое решение на платформе "1С:Предприятие 8.3" для комплексного управления вузом.
|
||||
- *Функции:* контингент студентов, личные дела и зачётные книги, учёт успеваемости и посещаемости, массовое формирование ведомостей, приёмная кампания, поддержка территориально распределённых баз.
|
||||
- *Ограничения:* тяжёлое ERP-решение, посещаемость вводится вручную через АРМ; нет студенческого QR-self-service и нативного веб-самообслуживания.
|
||||
- *Почему хуже для МИФИ:* монолитная проприетарная экосистема 1С; интеграция требует механизмов обмена 1С, а не REST/CAS.
|
||||
|
||||
**5. Прочие российские: Дневник.ру, ЭлЖур, "Сетевой город. Образование" (NetSchool), "Мой Класс", "Учебный учет"**
|
||||
- Это школьные электронные журналы или CRM для учебных центров: ручная отметка посещаемости, модель "класс/группа", без QR-self-service и без вузовской интеграции с CAS/реестрами.
|
||||
|
||||
### Зарубежные системы
|
||||
|
||||
**6. Microsoft Teams (attendance & engagement report)**
|
||||
- *Функции:* автоматический отчёт о посещаемости онлайн-встречи (имена, время входа/выхода, длительность участия); engagement-отчёт (реакции, поднятые руки, включённые камеры) — только в Teams Enterprise/Premium; экспорт в CSV.
|
||||
- *Ограничения:* фиксирует присутствие на **видеовстрече**, а не на очном занятии. По документации Microsoft, "If the event organizer leaves the org, reports are permanently deleted and can't be retrieved" — отчёты привязаны к организатору и безвозвратно удаляются при его уходе из организации. Также: "In meetings with more than 120 participants, the attendance report that's available during the meeting will only include a partial list of attendees. The post-meeting report will contain the full list" — во время встречи >120 участников виден лишь частичный список.
|
||||
- *Почему хуже для МИФИ:* считает онлайн-участие; нет QR для очных занятий; нет интеграции с CAS/реестрами МИФИ; нет вузовской ролевой модели.
|
||||
|
||||
**7. Google Classroom / Google Meet (attendance tracking)**
|
||||
- *Функции:* в самом Google Classroom **нативной отметки посещаемости нет** (распространённый обходной путь — Google Forms/Sheets). По справке Google Meet, отслеживание посещаемости доступно только на платных тарифах: "Attendance tracking is available to Google Workspace Essentials, Business Plus, Enterprise Starter, Enterprise Essentials, Enterprise Standard, Enterprise Plus, Education Plus and the Teaching and Learning Upgrade users"; пользователи Education Plus и Teaching and Learning Upgrade "automatically receive an attendance report for any meeting with two or more participants".
|
||||
- *Ограничения:* отсутствие встроенной посещаемости в Classroom; отчёт Meet формируется по онлайн-встрече и требует платной редакции; привязка к экосистеме Google Workspace.
|
||||
- *Почему хуже для МИФИ:* ключевая функция отсутствует/платная; нет очного QR; нет интеграции с внутренними системами вуза.
|
||||
|
||||
**8. Brightspace (D2L) — Attendance tool**
|
||||
- *Функции:* реестры посещаемости (registers), настраиваемые схемы статусов и порог тревоги. По документации D2L, системная схема (System Scheme) состоит из двух статусов — present и absent, а "Cause for Concern (%)" задаёт порог, при падении ниже которого в колонке процента посещаемости выводится предупреждение; есть интеграция с журналом оценок и e-mail. QR/мобильная самоотметка — **только через сторонние интеграции** (Qwickly Attendance, You-Attend) из D2L IntegrationHub.
|
||||
- *Ограничения:* базовый инструмент — электронный ledger; отметку ставит преподаватель/ассистент вручную (инструмент **не** self-reporting).
|
||||
- *Почему хуже для МИФИ:* требует внедрения всей LMS Brightspace плюс платных плагинов для QR; нет нативной интеграции с CAS МИФИ.
|
||||
|
||||
**9. Специализированные QR/чек-ин SaaS: OneTap, AccuClass (Engineerica), QR-Code-Generator, SuperQR, QR Attendee**
|
||||
- *Функции:* QR/штрихкод чек-ин, импорт из Excel, дашборды, отчёты, иногда геолокация.
|
||||
- *Ограничения:* SaaS "под мероприятия/школы/залы"; данные хранятся в облаке вендора; нет вузовской ролевой модели и интеграции с внутренними реестрами.
|
||||
- *Почему хуже для МИФИ:* vendor lock-in, хранение ПДн у внешнего провайдера, отсутствие SSO-интеграции с CAS и Outbox-синхронизации.
|
||||
|
||||
**10. Open-source проекты (GitHub: AzeemIdrisi/QR-Attendance-System, Gudleifr1/Check-by-QR и др.)**
|
||||
- *Функции:* генерация и сканирование QR, геопроверка, защита от повторной отметки и от устаревших кодов, HTTPS.
|
||||
- *Ограничения:* студенческие/хакатонные прототипы на разнородных стеках (Django, PHP/Bootstrap, Java/SQLite, .NET MAUI/C#); без промышленной надёжности доставки и без корпоративной интеграции.
|
||||
- *Почему хуже для МИФИ:* не production-grade; нет Outbox/CAS/ролевой модели.
|
||||
|
||||
**Сравнительная таблица (рекомендуемый формат для главы):**
|
||||
|
||||
| Система | QR-self-service | SSO/CAS-интеграция | Outbox-синхронизация | Вузовская ролевая модель | On-premise | Open-source |
|
||||
|---|---|---|---|---|---|---|
|
||||
| ВВГУ "Учёт посещаемости" | ✔ | ✘ | ✘ | частично | ✔ | ✘ |
|
||||
| БАРС.Образование | ✘ | ✘ (Госуслуги/СМЭВ) | ✘ | школьная | зависит | ✘ |
|
||||
| iSpring Learn | ✘ | ✔ (SSO) | ✘ | корпоративная | ✔ | ✘ |
|
||||
| 1С:Университет | ✘ | ✘ | ✘ (обмен 1С) | ✔ | ✔ | ✘ |
|
||||
| MS Teams | ✘ | ✔ (Azure AD) | ✘ | ✘ | ✘ | ✘ |
|
||||
| Google Classroom/Meet | ✘ | ✔ (Google) | ✘ | ✘ | ✘ | ✘ |
|
||||
| Brightspace | через плагины | ✔ | ✘ | LMS | ✔ | ✘ |
|
||||
| QR-SaaS (OneTap и др.) | ✔ | частично | ✘ | ✘ | ✘ | ✘ |
|
||||
| **Проектируемая система МИФИ** | **✔** | **✔ (CAS МИФИ)** | **✔** | **✔ (полная)** | **✔** | **✔** |
|
||||
|
||||
**Вывод по Части 1:** ниша "QR-самоотметка очной посещаемости + CAS-SSO вуза + Outbox-синхронизация с реестрами + полная ролевая модель + on-premise" не закрыта существующими продуктами — это и есть обоснование разработки собственной системы.
|
||||
|
||||
---
|
||||
|
||||
## ЧАСТЬ 2. ОБОСНОВАНИЕ ВЫБОРА ИНСТРУМЕНТАРИЯ (с источниками)
|
||||
|
||||
### 2.1 Веб-фреймворк: Ruby on Rails 7 (vs Django / Laravel / Spring)
|
||||
- *Обоснование:* Rails реализует строгий паттерн MVC, принципы "Convention over Configuration" и DRY, что ускоряет разработку CRUD-приложений; зрелая экосистема gem'ов; Hotwire включён в Rails 7 "из коробки". На Rails работают GitHub, Shopify, Basecamp.
|
||||
- *Честный контр-аргумент:* Ruby исторически уступает по "сырой" производительности оптимизированному PHP/Python и требует больше памяти; компенсируется фоновой обработкой, кэшированием и горизонтальным масштабированием.
|
||||
- *Русскоязычный источник:* Даньшин К.А. "Сравнительный анализ современных веб-фреймворков для разработки приложений" (CyberLeninka, 2019) — сравнение ASP.NET Core, Laravel, Spring, Ruby on Rails, Django. Альтернатива (ВАК): "Преимущества и недостатки фреймворков для разработки веб-приложений в целях цифровизации экономики" // "Научное обозрение. Технические науки".
|
||||
- *Раздел:* выбор бэкенд-фреймворка и архитектуры MVC.
|
||||
|
||||
### 2.2 Hotwire / Turbo / Stimulus (альтернатива SPA)
|
||||
- *Обоснование:* Hotwire ("HTML Over The Wire", автор — D. H. Hansson) отправляет фрагменты HTML вместо JSON, обеспечивая "SPA-ощущение" без тяжёлого клиентского JS. Компоненты: Turbo Drive (навигация без полной перезагрузки), Turbo Frames (независимые регионы), Turbo Streams (точечные/realtime-обновления, в т.ч. по WebSocket), Stimulus (минимальная интерактивность). Меньше передаваемых данных, быстрее загрузка, проще поддержка для backend-команды.
|
||||
- *Честный контр-аргумент:* в части независимых тестов прирост скорости навигации Turbo по сравнению с уже оптимизированными решениями оказывался незначительным; htmx даёт меньший бандл. Для backend-ориентированной команды и server-rendered приложения выигрыш Hotwire — прежде всего в DX и снижении объёма JS.
|
||||
- *Раздел:* фронтенд-архитектура; обоснование отказа от отдельного React/Vue SPA.
|
||||
|
||||
### 2.3 Фоновые задачи: Sidekiq + Redis
|
||||
- *Обоснование:* Sidekiq — многопоточный обработчик фоновых задач, использующий Redis (in-memory store) как хранилище очередей; интеграция с Active Job (`config.active_job.queue_adapter = :sidekiq`); очереди с приоритетами, авто-ретраи с экспоненциальной задержкой, веб-интерфейс мониторинга. Многопоточная модель эффективнее по памяти, чем процессные Resque/Delayed Job. Критично для Outbox-релея (асинхронная публикация событий) и тяжёлых операций (генерация отчётов, рассылки).
|
||||
- *Раздел:* асинхронная обработка; реализация Outbox-релея.
|
||||
|
||||
### 2.4 Outbox Pattern (надёжная доставка во внешние реестры)
|
||||
- *Обоснование:* решает проблему "dual write" — несогласованности при одновременной записи в БД и отправке сообщения брокеру. Событие записывается в outbox-таблицу **в той же транзакции**, что и бизнес-данные; отдельный процесс-релей (Sidekiq) публикует его наружу, обеспечивая гарантию доставки at-least-once; получатели должны быть **идемпотентны** (релей может опубликовать сообщение более одного раза). Устраняет необходимость в распределённых транзакциях (2PC).
|
||||
- *Источники (первичные/авторитетные):* Chris Richardson, microservices.io — "Pattern: Transactional outbox"; AWS Prescriptive Guidance — "Transactional outbox pattern"; Confluent Developer; книга Chris Richardson "Microservices Patterns".
|
||||
- *Раздел:* синхронизация с внешними реестрами МИФИ; надёжность данных.
|
||||
|
||||
### 2.5 Service Objects в Rails
|
||||
- *Обоснование:* вынос бизнес-логики из "толстых" моделей/контроллеров в PORO (plain old Ruby object) с единственным публичным методом `call`, размещаемые в `app/services`. Принципы: единая ответственность, тестируемость в изоляции, переиспользуемость, "skinny models, skinny controllers".
|
||||
- *Честный контр-аргумент:* часть сообщества (M. Fowler — критика "анемичной модели") считает массовые Service Objects антипаттерном, ведущим к процедурному стилю; применять для логики, которая действительно не принадлежит ни модели, ни контроллеру.
|
||||
- *Раздел:* организация бизнес-логики (генерация/валидация QR, проведение отметки, формирование Outbox-событий).
|
||||
|
||||
### 2.6 СУБД: PostgreSQL (vs MySQL / SQLite)
|
||||
- *Обоснование:* PostgreSQL — полностью ACID-совместимая СУБД с MVCC, расширенным набором типов, ролевой моделью доступа (role-based access control), SSL/TLS, логической репликацией (Publish/Subscribe); предпочтительна для сложных транзакционных приложений с большими объёмами данных.
|
||||
- *Рецензируемый источник:* Salunke S.V., Ouda A. "A Performance Benchmark for the PostgreSQL and MySQL Databases" // Future Internet (MDPI). 2024. Vol. 16(10). Art. 382. DOI: 10.3390/fi16100382. Ключевой результат: "PostgreSQL's execution time for 1 million records ranged from 0.6 ms to 0.8 ms, while MySQL's ranged from 9 ms to 12 ms, indicating that PostgreSQL is about 13 times faster"; для SELECT c WHERE — 0.09–0.13 мс против 0.9–1 мс (≈9× быстрее). Авторы отмечают: "Our quantified results show PostgreSQL's superior performance in select operations".
|
||||
- *Честный контр-аргумент:* результаты бенчмарков зависят от профиля нагрузки — в ряде независимых тестов (sysbench OLTP read-only) MySQL опережал PostgreSQL на простых SELECT на 20–30%. Абсолютное превосходство одной СУБД утверждать некорректно; выбор PostgreSQL мотивирован транзакционной целостностью и богатством типов, важными для Outbox и ролевой модели.
|
||||
- *Раздел:* выбор хранилища данных.
|
||||
|
||||
### 2.7 Namespaced Controllers (организация MVC под ролевую модель)
|
||||
- *Обоснование:* пространства имён контроллеров (`Admin::`, `Teacher::`, `Student::`, `Api::`) изолируют ролевые интерфейсы, упрощают маршрутизацию и авторизацию — естественная техническая поддержка вузовской ролевой модели (студент / преподаватель / куратор / деканат / администратор).
|
||||
- *Раздел:* архитектура контроллеров и авторизация.
|
||||
|
||||
### 2.8 CSS: Tailwind CSS (vs Bootstrap)
|
||||
- *Обоснование:* utility-first подход даёт существенно меньший production-бандл за счёт JIT-purge (по отраслевым обзорам — на 60–70% меньше, чем production-CSS Bootstrap), полную кастомизацию через `tailwind.config.js` и отсутствие навязанного "вида Bootstrap"; нативная интеграция с Rails 7 (`rails new myapp --css tailwind`). Tailwind не тянет JS-компоненты (Bootstrap поставляет ~20 КБ JS).
|
||||
- *Честный контр-аргумент:* более крутая кривая обучения и "шумная" разметка; Bootstrap быстрее для прототипа за счёт готовых компонентов и CDN-подключения.
|
||||
- *Раздел:* выбор UI-фреймворка.
|
||||
|
||||
### 2.9 SSO / CAS (Central Authentication Service)
|
||||
- *Обоснование:* CAS — протокол единого входа (SSO), при котором приложение **никогда не видит пароль** пользователя: аутентификация выполняется на доверенном центральном сервере, который выдаёт одноразовый service ticket, валидируемый приложением. CAS отвечает за аутентификацию, а **авторизация** — на стороне приложения (часто через атрибуты LDAP). Разработан в Yale University, ныне сопровождается Apereo Foundation; есть множество клиентских библиотек. Снижает риск фишинга за счёт единообразного входа.
|
||||
- *Раздел:* интеграция с корпоративной системой аутентификации МИФИ.
|
||||
|
||||
### 2.10 Академические источники по QR-посещаемости и ЭЖ вуза
|
||||
- **Nuhi A., Memeti A., Imeri F., Çiço B.** "Smart Attendance System using QR Code" // 2020 9th Mediterranean Conference on Embedded Computing (MECO), Budva, Montenegro, 2020, pp. 1–4. **DOI: 10.1109/MECO49872.2020.9134225** (IEEE Xplore). — прямое обоснование QR-подхода для учёта посещаемости в вузе.
|
||||
- **Глуховский К.С., Пирожков Р.В., Цвелик Е.А.** "Электронный журнал как элемент цифровой трансформации вуза" // Инженерный вестник Дона. 2021. №5 (URL ivdon.ru, ст. 6978; зеркало CyberLeninka). — отраслевой аналог в **ВИТИ НИЯУ МИФИ** (электронный дневник посещаемости/успеваемости как модуль ЭИОС); особо ценен ведомственной релевантностью.
|
||||
- Дополнительно (CyberLeninka, для контекста методов): "Система учёта посещаемости студентов на основе распознавания лиц" (RetinaFace/ResNet); "Методика автоматизации контроля посещаемости очных занятий…" (кафедра ИКТ РХТУ им. Д.И. Менделеева, Google-таблицы + QR-сканер).
|
||||
- Прочие IEEE по теме (для расширения обзора методов): "Class Attendance Recording using QR Code via Smartphone" (DOI 10.1109/...8912099); "Online Attendance Monitoring System Using QR Code (OAMS)".
|
||||
|
||||
---
|
||||
|
||||
## Recommendations
|
||||
1. **Часть 1 оформить сравнительной таблицей** (приведена выше): столбцы — QR-self-service / SSO-CAS / Outbox / ролевая модель / on-premise / open-source. Это наглядно демонстрирует незакрытую нишу и служит логическим мостиком к постановке задачи.
|
||||
2. **Для каждого тех-решения — минимум один авторитетный источник.** Для Outbox и CAS использовать первичные источники (microservices.io / Chris Richardson; Apereo/Yale), для QR — IEEE (Nuhi et al., 2020), для СУБД — рецензируемый MDPI-бенчмарк (Salunke & Ouda, 2024).
|
||||
3. **Усилить русскоязычную базу под требования РИНЦ/ВАК:** к подтверждённым (Глуховский 2021; Даньшин 2019) добавить с eLibrary 2–3 статьи по паттернам Rails/Service Objects и по СУБД; для статьи Даньшина (2019) **верифицировать журнал, номер и страницы** на CyberLeninka/eLibrary перед финальным оформлением.
|
||||
4. **Чётко разделить типы источников** в списке литературы: рецензируемые научные (IEEE, MDPI, CyberLeninka/ВАК) vs технические/отраслевые (официальная документация Rails, Microsoft Learn, D2L, Apereo). Для коммерческих систем указывать **дату обращения** (функции Teams Premium и Google Education Plus меняются).
|
||||
5. **Пороговые критерии, меняющие выводы:** если бы существовал продукт с нативным CAS-коннектором МИФИ + QR-self-service + документированным Outbox/идемпотентным экспортом в реестры — разработка собственной системы была бы неоправданна; ни один из рассмотренных аналогов этому набору не удовлетворяет, что и фиксирует обоснованность работы.
|
||||
|
||||
## Caveats
|
||||
- **Небиблиометричность части источников.** Материалы по Hotwire, Tailwind, Sidekiq и Service Objects — преимущественно официальная документация и технические блоги, а не рецензируемые публикации; в УИР их следует помечать как технические/отраслевые источники, а научную аргументацию строить вокруг рецензируемых работ (IEEE, MDPI, ВАК/РИНЦ).
|
||||
- **Противоречивость бенчмарков СУБД.** PostgreSQL уверенно выигрывает на сложных транзакциях и в цитируемом бенчмарке Salunke & Ouda (до ~13× на больших выборках), однако на простых OLTP-SELECT MySQL в ряде независимых тестов опережает PostgreSQL на 20–30%; нельзя утверждать абсолютное превосходство — выбор мотивирован транзакционной целостностью и типами данных.
|
||||
- **Производительность Ruby/Rails** исторически ниже, чем у оптимизированного PHP/Python; обоснование Rails строится на скорости разработки и DX, а не на "сырой" скорости рантайма, — это важно сформулировать честно.
|
||||
- **Неполные библиографические поля** статьи Даньшина (2019): подтверждены автор, название, год; журнал/номер/страницы требуют верификации на первоисточнике. Для статьи Глуховского и др. (2021) журнал "Инженерный вестник Дона" — онлайн-издание, использующее номера статей (ст. 6978), без классической пагинации и DOI.
|
||||
- **Изменчивость функций коммерческих систем.** Привязки тарифов (Teams Premium, Google Education Plus / Teaching and Learning Upgrade) и набор функций периодически меняются — в работе обязательно указывать дату обращения к источнику.
|
||||
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 228 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 557 KiB |
@@ -9,37 +9,26 @@ aliases: []
|
||||
# Excalidraw Data
|
||||
|
||||
## Text Elements
|
||||
|
||||
mini-pc N150 / 12 GB / 256 GB ^mini_lbl
|
||||
|
||||
cloud-pc i5 / 16 GB / 256 GB + 5 TB ^gihc0eNQ
|
||||
|
||||
Internet ^JF8gyxg7
|
||||
|
||||
NPM (публичный) ^De5FvlCO
|
||||
|
||||
Portainer Agent ^aa6HjhlV
|
||||
Caddy (публичный) ^De5FvlCO
|
||||
|
||||
ZeroTier ^m9o2XfZR
|
||||
|
||||
ufw ^irwhPywz
|
||||
|
||||
Nginx (80, 443) ^3YCkaaEW
|
||||
|
||||
ZeroTier overlay network ^5VEs4mhQ
|
||||
|
||||
AdGuard Home ^14mDr3CI
|
||||
|
||||
NPM (локальный) ^Oynv3oBy
|
||||
Caddy (локальный) ^Oynv3oBy
|
||||
|
||||
Vaultwarden ^0uUIYjrp
|
||||
|
||||
ZeroTier ^8m3hcPU4
|
||||
|
||||
Portainer Agent ^O5TryriF
|
||||
|
||||
Cockpit ^YY7Tjxu0
|
||||
|
||||
Gitea Actions Runner ^bC30cure
|
||||
|
||||
Gitea + Registry ^K59um5ww
|
||||
@@ -50,13 +39,25 @@ ZeroTier ^WzKFf7RD
|
||||
|
||||
SMB/BU/Files (5 TB HDD) ^HZKjRf2E
|
||||
|
||||
Portainer Server ^7rAi7eTS
|
||||
|
||||
Cockpit ^aVjmA1Ms
|
||||
|
||||
|
||||
^Jq0JkEPn
|
||||
|
||||
Gitea Actions Runner ^CM1Gp9xI
|
||||
|
||||
Gitea Actions Runner ^8PRErTQ6
|
||||
|
||||
NixOS ^DQNLMdIF
|
||||
|
||||
NixOS ^gTVW2le6
|
||||
|
||||
mini-pc N150 / 12 GB / 256 GB ^mini_lbl
|
||||
|
||||
157.22.231.198 ^eXMuxq0w
|
||||
|
||||
192.168.1.10 ^7IGqv1Hi
|
||||
|
||||
192.168.1.5 ^ho5T6rsv
|
||||
|
||||
Infrastructure — ada-dev ^title
|
||||
|
||||
ru-vps (белый IP) ^vps_lbl
|
||||
@@ -64,150 +65,174 @@ ru-vps (белый IP) ^vps_lbl
|
||||
%%
|
||||
## Drawing
|
||||
```compressed-json
|
||||
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=
|
||||
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
|
||||
```
|
||||
%%
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
generated: 2026-06-05
|
||||
total: 505
|
||||
generated: 2026-06-16
|
||||
total: 520
|
||||
---
|
||||
|
||||
# Vault Index
|
||||
@@ -9,23 +9,17 @@ total: 505
|
||||
|
||||
| Параметр | Значение |
|
||||
|----------|----------|
|
||||
| Всего заметок | 505 |
|
||||
| Всего заметок | 520 |
|
||||
|
||||
## .
|
||||
|
||||
| Заметка | Aliases | Status | Type | Tags |
|
||||
|---------|---------|--------|------|------|
|
||||
| [[AGENTS]] | Агент — контекст и инструкции для этого хранилища | — | — | [] |
|
||||
| [[AGENTS]] | Агент — контекст и инструкции для этого хранилища | stable | guide | [agent, vault] |
|
||||
| [[CLAUDE]] | CLAUDE | — | — | [] |
|
||||
| [[CODEX]] | CODEX | — | — | [] |
|
||||
| [[GEMINI]] | GEMINI | — | — | [] |
|
||||
|
||||
## 00 Inbox
|
||||
|
||||
| Заметка | Aliases | Status | Type | Tags |
|
||||
|---------|---------|--------|------|------|
|
||||
| [[Вопросы по Вебу]] | — | processing | concept | [] |
|
||||
|
||||
## 01 Ideas
|
||||
|
||||
| Заметка | Aliases | Status | Type | Tags |
|
||||
@@ -45,6 +39,7 @@ total: 505
|
||||
|---------|---------|--------|------|------|
|
||||
| [[Анализ рынка систем учета посещаемости]] | Анализ рынка систем учета посещаемости | seed | concept | [] |
|
||||
| [[НИР]] | НИР | seed | concept | [] |
|
||||
| [[УИР_Сводный_вариант]] | — | stable | document | [уир, мифи, разработка] |
|
||||
|
||||
## 02 Projects/Наука/Статьи
|
||||
|
||||
@@ -210,6 +205,7 @@ total: 505
|
||||
|
||||
| Заметка | Aliases | Status | Type | Tags |
|
||||
|---------|---------|--------|------|------|
|
||||
| [[CI-CD — основы]] | Основы CI/CD, Непрерывная интеграция и доставка | processing | concept | [devops, cicd, gitlab-ci, jenkins] |
|
||||
| [[DevOps - основы]] | DevOps - основы | stable | concept | [devops] |
|
||||
| [[Git]] | Git | seed | concept | [devops, git, vcs] |
|
||||
| [[Gitlab]] | Gitlab | seed | concept | [devops, git, cicd, gitlab] |
|
||||
@@ -325,6 +321,7 @@ total: 505
|
||||
| Заметка | Aliases | Status | Type | Tags |
|
||||
|---------|---------|--------|------|------|
|
||||
| [[Infrastructure — ada-dev]] | Infrastructure — ada-dev | seed | concept | [homelab, infrastructure, devops] |
|
||||
| [[Proxmox VE — local, local-lvm, LVM]] | — | seed | guide | [homelab, proxmox, lvm, storage] |
|
||||
| [[Проблемы дистрибутивов]] | Проблемы дистрибутивов | seed | concept | [homelab] |
|
||||
|
||||
## 90 Library/HomeLab/Proxy
|
||||
@@ -344,6 +341,7 @@ total: 505
|
||||
| [[File Permissions]] | File Permissions | stable | concept | [os, linux] |
|
||||
| [[Linux - MOC]] | Linux - MOC | stable | moc | [admin, os, linux] |
|
||||
| [[Redirects]] | Redirects | stable | concept | [os, linux] |
|
||||
| [[Sudoers]] | sudoers, Настройка sudo | seed | guide | [linux, sudo, security] |
|
||||
| [[Super-user]] | Super-user | stable | concept | [os, linux] |
|
||||
|
||||
## 90 Library/Machine Learning
|
||||
@@ -408,7 +406,7 @@ total: 505
|
||||
| Заметка | Aliases | Status | Type | Tags |
|
||||
|---------|---------|--------|------|------|
|
||||
| [[Дискриминантный анализ - MOC]] | Дискриминантный анализ - MOC | stable | moc | [math, statistics, discriminant-analysis] |
|
||||
| [[Параметрический дискриминантный анализ в случае нормального закона распределения классов]] | Параметрический Дискриминантный Анализ В Случае Нормального Закона Распределения | seed | concept | [math, statistics, discriminant-analysis, ml-algorithms] |
|
||||
| [[Параметрический дискриминантный анализ в случае нормального закона распределения классов]] | Параметрический Дискриминантный Анализ В Случае Нормального Закона Распределения Классов | seed | concept | [math, statistics, discriminant-analysis, ml-algorithms] |
|
||||
| [[Построение оптимальной процедуры классификации]] | Построение Оптимальной Процедуры Классификации | seed | concept | [math, mephi, statistics, discriminant-analysis] |
|
||||
| [[Принципы дискриминантного анализа]] | Принципы Дискриминантного Анализа | seed | concept | [math, mephi, statistics, discriminant-analysis] |
|
||||
| [[Условие отнесения объекта к классу в случае двух признаков]] | Условие Отнесения Объекта К Классу В Случае Двух Признаков | stable | concept | [math, ml, mephi, statistics, discriminant-analysis] |
|
||||
@@ -452,7 +450,7 @@ total: 505
|
||||
| [[ФА - модель, цель, сущность]] | ФА - Модель, Цель, Сущность | stable | concept | [math, ml, mephi, statistics, factor-analysis] |
|
||||
| [[Факторное отображение. Факторная структура]] | Факторное Отображение. Факторная Структура | stable | concept | [math, mephi, statistics, factor-analysis] |
|
||||
| [[Факторный анализ]] | Факторный Анализ | stable | concept | [math, ml, mephi, statistics, factor-analysis] |
|
||||
| [[Факторный анализ. Цели и сущность. Постановка задачи. Отличие от анализа главных компонент]] | Факторный Анализ. Цели И Сущность. Постановка Задачи. Отличие От Анализа Главных | seed | concept | [math, mephi, statistics, factor-analysis] |
|
||||
| [[Факторный анализ. Цели и сущность. Постановка задачи. Отличие от анализа главных компонент]] | Факторный Анализ. Цели И Сущность. Постановка Задачи. Отличие От Анализа Главных Компонент | seed | concept | [math, mephi, statistics, factor-analysis] |
|
||||
| [[Фундаментальная теорема факторного анализа]] | Фундаментальная Теорема Факторного Анализа | stable | concept | [math, mephi, statistics, factor-analysis] |
|
||||
| [[Этапы ФА. Выделение факторов]] | Этапы ФА. Выделение Факторов | seed | concept | [math, mephi, statistics, factor-analysis] |
|
||||
|
||||
@@ -473,16 +471,19 @@ total: 505
|
||||
|
||||
| Заметка | Aliases | Status | Type | Tags |
|
||||
|---------|---------|--------|------|------|
|
||||
| [[Defense-in-depth]] | DiD, Глубокоэшелонированная защита | seed | concept | [networking, security, infosec, defense-in-depth] |
|
||||
| [[Ethernet]] | Ethernet | stable | concept | [network, networking, ethernet] |
|
||||
| [[LAN vs WAN]] | LAN Vs WAN | stable | concept | [network, networking] |
|
||||
| [[MAC-адрес]] | MAC-адрес | stable | concept | [networking] |
|
||||
| [[NAT]] | NAT | processing | concept | [network, networking, nat] |
|
||||
| [[Networking - MOC]] | Networking - MOC | stable | moc | [network, networking] |
|
||||
| [[OAuth 2.0]] | OAuth 2, OAuth 2.0 | processing | concept | [web, security, oauth, authorization] |
|
||||
| [[SASE]] | Secure Access Service Edge | seed | concept | [networking, security, infosec, sase] |
|
||||
| [[VLSM]] | VLSM | stable | concept | [network, networking] |
|
||||
| [[VPN]] | VPN | stable | concept | [network, networking, vpn] |
|
||||
| [[Zero Trust Network Access]] | ZTNA | seed | concept | [networking, security, infosec, ztna] |
|
||||
| [[Базовая настройка коммутатора]] | Базовая Настройка Коммутатора | stable | concept | [network, networking] |
|
||||
| [[Введение в сетевую безопасность]] | Введение в сетевую безопасность | seed | concept | [networking, security, infosec] |
|
||||
| [[Введение в сетевую безопасность]] | Введение в сетевую безопасность | processing | concept | [networking, security, infosec, vulnerability-scanning] |
|
||||
| [[Веб-аутентификация и авторизация]] | Идентификация, аутентификация и авторизация, Веб-аутентификация | processing | concept | [web, security, authentication, authorization, oauth] |
|
||||
| [[Витая пара]] | Витая Пара | stable | concept | [network, networking] |
|
||||
| [[Иерархическая модель Cisco]] | Иерархическая Модель Cisco | stable | concept | [network, networking] |
|
||||
@@ -614,6 +615,9 @@ total: 505
|
||||
|---------|---------|--------|------|------|
|
||||
| [[31, 32 - Налоги]] | 31, 32 - Налоги | seed | knowledge | [study, taxes] |
|
||||
| [[31, 32 - Налоги 1]] | 31, 32 - Налоги | seed | knowledge | [study, taxes] |
|
||||
| [[CI-CD Конвейер для сборки и доставки продукта. Построение пайплайнов]] | — | seed | concept | [] |
|
||||
| [[CIСD Конвейер для сборки и доставки продукта. Знакомство с GitlabCI и Jenkins]] | — | seed | concept | [] |
|
||||
| [[gemini-code-1780921978295]] | — | — | — | [] |
|
||||
| [[Git]] | Git | seed | concept | [] |
|
||||
| [[Gitlab]] | Gitlab | seed | concept | [] |
|
||||
| [[HW Eng17.03]] | HW Eng17.03 | seed | concept | [homework] |
|
||||
@@ -621,18 +625,22 @@ total: 505
|
||||
| [[HW Eng24.03]] | — | seed | concept | [] |
|
||||
| [[Infrastructure]] | Infrastructure | seed | concept | [] |
|
||||
| [[OSINT - Зачет]] | OSINT - Зачет | processing | knowledge | [mephi, study, osi] |
|
||||
| [[Proxmox VE — local, local-lvm, LVM — source]] | — | seed | concept | [] |
|
||||
| [[Rails 8 Migration Guide (finreport-analyzer)]] | — | seed | concept | [] |
|
||||
| [[Rails credentials edit не открывает редактор]] | Rails credentials edit не открывает редактор | seed | concept | [] |
|
||||
| [[reader]] | — | seed | concept | [] |
|
||||
| [[Ruby - массивы]] | Ruby - массивы | seed | concept | [] |
|
||||
| [[Ruby - основы языка]] | Ruby - основы языка | seed | concept | [] |
|
||||
| [[Sudoers — source]] | sudoers, Настройка sudo | seed | guide | [linux, sudo, security] |
|
||||
| [[Upgrade Homelab]] | Upgrade Homelab | processing | project | [homelab, hardware] |
|
||||
| [[Алгоритм обратного распространения ошибки для произвольного числа слоев НС]] | Алгоритм обратного распространения ошибки для произвольного числа слоев НС | seed | concept | [] |
|
||||
| [[Алгоритм обучения сети Кохонена]] | Алгоритм обучения сети Кохонена | seed | concept | [] |
|
||||
| [[Анализ типа фин. устойчивости]] | Анализ типа фин. устойчивости | seed | concept | [] |
|
||||
| [[Безопасность инфраструктуры. ZTNA, SASE, DiD]] | — | seed | concept | [] |
|
||||
| [[В случае ошибок на принтере Kyocera FS-1020MFP]] | В случае ошибок на принтере Kyocera FS-1020MFP | seed | concept | [] |
|
||||
| [[Введение в сетевую безопасность]] | Введение в сетевую безопасность | processing | concept | [] |
|
||||
| [[ВДО no кредитам]] | — | seed | concept | [] |
|
||||
| [[Вопросы по Вебу]] | — | processing | concept | [] |
|
||||
| [[Знания Ruby]] | Знания Ruby | seed | concept | [] |
|
||||
| [[Идеи для статьи]] | Идеи Для Статьи | — | — | [] |
|
||||
| [[Идея]] | — | seed | concept | [] |
|
||||
@@ -651,6 +659,7 @@ total: 505
|
||||
| [[Налоги - СР2]] | Налоги - СР2 | seed | knowledge | [study, taxes] |
|
||||
| [[Нейронная сеть Кохонена]] | Нейронная сеть Кохонена | seed | concept | [] |
|
||||
| [[Нейронная сеть хемминга]] | Нейронная сеть хемминга | seed | concept | [] |
|
||||
| [[Обзор аналогов и обоснование инструментария системы учёта посещаемости студентов НИЯУ МИФИ]] | — | — | — | [] |
|
||||
| [[Обратное распространение ошибки - вывод]] | Обратное распространение ошибки - вывод | seed | concept | [] |
|
||||
| [[Основные команды git]] | Основные команды git | processing | concept | [] |
|
||||
| [[Подготовка Speaking]] | Подготовка Speaking | seed | concept | [] |
|
||||
@@ -825,3 +834,9 @@ total: 505
|
||||
| [[Daily Note]] | — | — | daily | [daily] |
|
||||
| [[Study Item]] | — | seed | knowledge | [] |
|
||||
| [[Travel Item]] | — | seed | knowledge | [travel] |
|
||||
|
||||
## 99 System/Tools/agent-tools
|
||||
|
||||
| Заметка | Aliases | Status | Type | Tags |
|
||||
|---------|---------|--------|------|------|
|
||||
| [[AGENT_TOOLS]] | — | stable | guide | [agent, tooling, vault] |
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
title: Agent tools для vault
|
||||
status: stable
|
||||
type: guide
|
||||
tags: [agent, tooling, vault]
|
||||
created: 2026-06-16
|
||||
updated: 2026-06-16
|
||||
aliases: []
|
||||
---
|
||||
|
||||
# Agent tools для vault
|
||||
|
||||
Набор утилит для ИИ-агентов. Вывод — JSON, чтобы агент мог читать результат без парсинга Markdown-таблиц.
|
||||
|
||||
Запускать из корня vault:
|
||||
|
||||
```bash
|
||||
python3 "99 System/Tools/agent-tools/vault_index.py" dump
|
||||
```
|
||||
|
||||
## Команды
|
||||
|
||||
### `vault_index.py`
|
||||
|
||||
- `dump` — вывести все заметки из vault как JSON.
|
||||
- `query --term "CI/CD" --limit 10` — найти близкие заметки.
|
||||
- `rebuild` — пересобрать `99 System/INDEX.md`. Это write-команда.
|
||||
|
||||
### `vault_ingest.py`
|
||||
|
||||
- `scan` — показать файлы из `00 Inbox/` и `01 Ideas/`.
|
||||
- `classify PATH` — эвристически классифицировать источник.
|
||||
- `plan PATH` — выдать JSON-план ingest: назначение, похожие заметки, возможные wikilinks.
|
||||
|
||||
Команды read-only. Они не заменяют обязательный план и подтверждение перед workflow.
|
||||
|
||||
### `vault_audit.py`
|
||||
|
||||
- `broken-links` — найти wikilinks на несуществующие заметки.
|
||||
- `orphans` — найти заметки без входящих ссылок.
|
||||
- `source-links` — проверить wikilinks в `source`.
|
||||
- `components` — найти компоненты графа ссылок.
|
||||
- `all` — выполнить все проверки.
|
||||
|
||||
Команды read-only.
|
||||
|
||||
### `vault_frontmatter.py`
|
||||
|
||||
- `check [PATH ...]` — проверить обязательные поля и допустимые `status/type`.
|
||||
- `normalize PATH ...` — нормализовать frontmatter выбранных файлов. Это write-команда.
|
||||
|
||||
## Правила для агентов
|
||||
|
||||
- Перед `ingest`, `wiki`, `audit`, `remind`, `query` сначала читать `99 System/INDEX.md`; скрипты помогают, но не отменяют правила `AGENTS.md`.
|
||||
- Write-команды запускать только когда workflow уже подтверждён пользователем или правило явно требует обновить индекс.
|
||||
- `vault_ingest.py plan` — вспомогательный план, не окончательное решение. Агент обязан проверить содержимое источника сам.
|
||||
- После создания, удаления, перемещения заметки или изменения frontmatter запускать `vault_index.py rebuild`.
|
||||
@@ -0,0 +1,150 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from collections import Counter, defaultdict
|
||||
|
||||
from vault_common import all_records, json_print, known_note_names, source_values, wikilinks
|
||||
|
||||
|
||||
def broken_links(records: list[dict[str, object]]) -> list[dict[str, object]]:
|
||||
known = known_note_names(records)
|
||||
result = []
|
||||
for record in records:
|
||||
links = wikilinks(record["body"])
|
||||
missing = sorted({link for link in links if link not in known})
|
||||
if missing:
|
||||
result.append({"path": record["path"], "missing": missing})
|
||||
return result
|
||||
|
||||
|
||||
def orphans(records: list[dict[str, object]]) -> list[dict[str, object]]:
|
||||
incoming: Counter[str] = Counter()
|
||||
aliases: dict[str, str] = {}
|
||||
path_by_name: dict[str, str] = {}
|
||||
|
||||
for record in records:
|
||||
path_by_name[record["name"]] = record["path"]
|
||||
aliases[record["name"]] = record["name"]
|
||||
aliases[record["path"]] = record["name"]
|
||||
for alias in record["aliases"]:
|
||||
aliases[alias] = record["name"]
|
||||
|
||||
for record in records:
|
||||
for link in wikilinks(record["body"]):
|
||||
target = aliases.get(link)
|
||||
if target and target != record["name"]:
|
||||
incoming[target] += 1
|
||||
|
||||
result = []
|
||||
for name, path in sorted(path_by_name.items(), key=lambda item: item[1]):
|
||||
if incoming[name] == 0 and not path.startswith("99 System/Archive/"):
|
||||
result.append({"path": path, "name": name, "incoming": 0})
|
||||
return result
|
||||
|
||||
|
||||
def source_links(records: list[dict[str, object]]) -> dict[str, object]:
|
||||
known = known_note_names(records)
|
||||
linked = []
|
||||
missing = []
|
||||
plain = []
|
||||
|
||||
for record in records:
|
||||
for source in source_values(record["frontmatter"].get("source")):
|
||||
links = wikilinks(source)
|
||||
if links:
|
||||
for link in links:
|
||||
item = {"path": record["path"], "source": source, "target": link}
|
||||
if link in known:
|
||||
linked.append(item)
|
||||
else:
|
||||
missing.append(item)
|
||||
else:
|
||||
plain.append({"path": record["path"], "source": source})
|
||||
|
||||
return {"linked": linked, "missing": missing, "plain": plain}
|
||||
|
||||
|
||||
def graph_components(records: list[dict[str, object]]) -> list[dict[str, object]]:
|
||||
known = known_note_names(records)
|
||||
name_by_link = {}
|
||||
for record in records:
|
||||
name_by_link[record["name"]] = record["name"]
|
||||
name_by_link[record["path"]] = record["name"]
|
||||
for alias in record["aliases"]:
|
||||
name_by_link[alias] = record["name"]
|
||||
|
||||
graph: dict[str, set[str]] = defaultdict(set)
|
||||
path_by_name = {record["name"]: record["path"] for record in records}
|
||||
for record in records:
|
||||
name = record["name"]
|
||||
graph.setdefault(name, set())
|
||||
for link in wikilinks(record["body"]):
|
||||
if link not in known:
|
||||
continue
|
||||
target = name_by_link.get(link)
|
||||
if target and target != name:
|
||||
graph[name].add(target)
|
||||
graph[target].add(name)
|
||||
|
||||
seen = set()
|
||||
components = []
|
||||
for name in sorted(graph):
|
||||
if name in seen:
|
||||
continue
|
||||
stack = [name]
|
||||
component = []
|
||||
seen.add(name)
|
||||
while stack:
|
||||
current = stack.pop()
|
||||
component.append(current)
|
||||
for next_name in graph[current]:
|
||||
if next_name not in seen:
|
||||
seen.add(next_name)
|
||||
stack.append(next_name)
|
||||
components.append(
|
||||
{
|
||||
"size": len(component),
|
||||
"notes": [path_by_name[item] for item in sorted(component)[:20]],
|
||||
"truncated": len(component) > 20,
|
||||
}
|
||||
)
|
||||
|
||||
return sorted(components, key=lambda item: item["size"])
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Agent-facing vault audit helper")
|
||||
sub = parser.add_subparsers(dest="command", required=True)
|
||||
sub.add_parser("broken-links")
|
||||
sub.add_parser("orphans")
|
||||
sub.add_parser("source-links")
|
||||
sub.add_parser("components")
|
||||
sub.add_parser("all")
|
||||
args = parser.parse_args()
|
||||
|
||||
records = all_records()
|
||||
if args.command == "broken-links":
|
||||
json_print({"ok": True, "broken_links": broken_links(records)})
|
||||
elif args.command == "orphans":
|
||||
json_print({"ok": True, "orphans": orphans(records)})
|
||||
elif args.command == "source-links":
|
||||
json_print({"ok": True, "source_links": source_links(records)})
|
||||
elif args.command == "components":
|
||||
json_print({"ok": True, "components": graph_components(records)})
|
||||
elif args.command == "all":
|
||||
json_print(
|
||||
{
|
||||
"ok": True,
|
||||
"broken_links": broken_links(records),
|
||||
"orphans": orphans(records),
|
||||
"source_links": source_links(records),
|
||||
"components": graph_components(records),
|
||||
}
|
||||
)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,279 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
from datetime import date, datetime
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
INDEX_PATH = ROOT / "99 System" / "INDEX.md"
|
||||
EXCLUDED_DIRS = {".git", ".trash", ".obsidian", ".smart-env"}
|
||||
REQUIRED_FRONTMATTER = {"title", "status", "type", "tags", "created", "updated"}
|
||||
VALID_STATUS = {"seed", "processing", "stable"}
|
||||
VALID_TYPE = {"concept", "moc", "lab", "knowledge", "guide"}
|
||||
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except Exception: # pragma: no cover - depends on agent environment
|
||||
yaml = None
|
||||
|
||||
|
||||
def rel(path: Path) -> str:
|
||||
return path.relative_to(ROOT).as_posix()
|
||||
|
||||
|
||||
def json_print(data: Any) -> None:
|
||||
print(json.dumps(data, ensure_ascii=False, indent=2, sort_keys=True))
|
||||
|
||||
|
||||
def note_files(include_index: bool = False) -> list[Path]:
|
||||
files: list[Path] = []
|
||||
for path in ROOT.rglob("*.md"):
|
||||
parts = set(path.relative_to(ROOT).parts)
|
||||
if parts & EXCLUDED_DIRS:
|
||||
continue
|
||||
if not include_index and path == INDEX_PATH:
|
||||
continue
|
||||
files.append(path)
|
||||
return sorted(files, key=lambda p: rel(p).lower())
|
||||
|
||||
|
||||
def split_frontmatter(text: str) -> tuple[dict[str, Any], str, str | None]:
|
||||
lines = text.splitlines()
|
||||
if not lines or lines[0].strip() != "---":
|
||||
return {}, text, None
|
||||
|
||||
end = None
|
||||
for index, line in enumerate(lines[1:], start=1):
|
||||
if line.strip() == "---":
|
||||
end = index
|
||||
break
|
||||
|
||||
if end is None:
|
||||
return {}, text, None
|
||||
|
||||
raw = "\n".join(lines[1:end])
|
||||
body = "\n".join(lines[end + 1 :])
|
||||
return parse_frontmatter(raw), body, raw
|
||||
|
||||
|
||||
def parse_frontmatter(raw: str) -> dict[str, Any]:
|
||||
if yaml is not None:
|
||||
try:
|
||||
data = yaml.safe_load(raw) or {}
|
||||
return data if isinstance(data, dict) else {}
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
data: dict[str, Any] = {}
|
||||
current_key: str | None = None
|
||||
|
||||
for line in raw.splitlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
|
||||
list_item = re.match(r"^\s*-\s+(.*)$", line)
|
||||
if list_item and current_key:
|
||||
data.setdefault(current_key, [])
|
||||
if isinstance(data[current_key], list):
|
||||
data[current_key].append(clean_scalar(list_item.group(1)))
|
||||
continue
|
||||
|
||||
pair = re.match(r"^([A-Za-zА-Яа-я0-9_-]+):\s*(.*)$", line)
|
||||
if not pair:
|
||||
continue
|
||||
|
||||
key, value = pair.group(1), pair.group(2).strip()
|
||||
current_key = key
|
||||
if value == "":
|
||||
data[key] = [] if key in {"aliases", "tags"} else ""
|
||||
elif value.startswith("[") and value.endswith("]"):
|
||||
data[key] = parse_inline_list(value)
|
||||
else:
|
||||
data[key] = clean_scalar(value)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def parse_inline_list(value: str) -> list[str]:
|
||||
inner = value.strip()[1:-1].strip()
|
||||
if not inner:
|
||||
return []
|
||||
return [clean_scalar(item.strip()) for item in inner.split(",") if item.strip()]
|
||||
|
||||
|
||||
def clean_scalar(value: Any) -> str:
|
||||
text = str(value).strip()
|
||||
if len(text) >= 2 and text[0] == text[-1] and text[0] in {"'", '"'}:
|
||||
return text[1:-1]
|
||||
return text
|
||||
|
||||
|
||||
def as_list(value: Any) -> list[str]:
|
||||
if value is None or value == "":
|
||||
return []
|
||||
if isinstance(value, list):
|
||||
return [clean_scalar(item) for item in value if clean_scalar(item)]
|
||||
return [clean_scalar(value)]
|
||||
|
||||
|
||||
def as_cell(value: Any, default: str = "—") -> str:
|
||||
if value is None or value == "":
|
||||
return default
|
||||
if isinstance(value, (date, datetime)):
|
||||
return value.isoformat()
|
||||
return str(value)
|
||||
|
||||
|
||||
def tags_cell(value: Any) -> str:
|
||||
tags = as_list(value)
|
||||
return "[" + ", ".join(tags) + "]"
|
||||
|
||||
|
||||
def note_record(path: Path) -> dict[str, Any]:
|
||||
text = path.read_text(encoding="utf-8")
|
||||
fm, body, raw_fm = split_frontmatter(text)
|
||||
return {
|
||||
"path": rel(path),
|
||||
"name": path.stem,
|
||||
"title": as_cell(fm.get("title"), ""),
|
||||
"aliases": as_list(fm.get("aliases")),
|
||||
"status": as_cell(fm.get("status")),
|
||||
"type": as_cell(fm.get("type")),
|
||||
"tags": as_list(fm.get("tags")),
|
||||
"created": as_cell(fm.get("created"), ""),
|
||||
"updated": as_cell(fm.get("updated"), ""),
|
||||
"frontmatter": fm,
|
||||
"has_frontmatter": raw_fm is not None,
|
||||
"body": body,
|
||||
}
|
||||
|
||||
|
||||
def all_records() -> list[dict[str, Any]]:
|
||||
return [note_record(path) for path in note_files()]
|
||||
|
||||
|
||||
def wikilinks(text: str) -> list[str]:
|
||||
text = strip_markdown_code(text)
|
||||
links = []
|
||||
for match in re.finditer(r"\[\[([^\]]+)\]\]", text):
|
||||
target = match.group(1).split("|", 1)[0].split("#", 1)[0].strip()
|
||||
if target:
|
||||
links.append(target)
|
||||
return links
|
||||
|
||||
|
||||
def strip_markdown_code(text: str) -> str:
|
||||
text = re.sub(r"```.*?```", "", text, flags=re.DOTALL)
|
||||
text = re.sub(r"`[^`\n]*`", "", text)
|
||||
return text
|
||||
|
||||
|
||||
def known_note_names(records: list[dict[str, Any]]) -> set[str]:
|
||||
names: set[str] = set()
|
||||
for record in records:
|
||||
names.add(record["name"])
|
||||
names.add(record["path"])
|
||||
names.update(record["aliases"])
|
||||
return names
|
||||
|
||||
|
||||
def tokenize(value: str) -> set[str]:
|
||||
return {token.lower() for token in re.findall(r"[A-Za-zА-Яа-я0-9_+-]+", value)}
|
||||
|
||||
|
||||
def search_records(records: list[dict[str, Any]], term: str, limit: int = 10) -> list[dict[str, Any]]:
|
||||
query = tokenize(term)
|
||||
if not query:
|
||||
return []
|
||||
|
||||
scored = []
|
||||
for record in records:
|
||||
haystack = " ".join(
|
||||
[
|
||||
record["name"],
|
||||
record["title"],
|
||||
" ".join(record["aliases"]),
|
||||
" ".join(record["tags"]),
|
||||
record["path"],
|
||||
]
|
||||
)
|
||||
tokens = tokenize(haystack)
|
||||
score = len(query & tokens)
|
||||
if term.lower() in haystack.lower():
|
||||
score += 3
|
||||
if score:
|
||||
scored.append((score, record))
|
||||
|
||||
scored.sort(key=lambda item: (-item[0], item[1]["path"]))
|
||||
return [compact_record(record) | {"score": score} for score, record in scored[:limit]]
|
||||
|
||||
|
||||
def compact_record(record: dict[str, Any]) -> dict[str, Any]:
|
||||
return {
|
||||
"path": record["path"],
|
||||
"name": record["name"],
|
||||
"title": record["title"],
|
||||
"aliases": record["aliases"],
|
||||
"status": record["status"],
|
||||
"type": record["type"],
|
||||
"tags": record["tags"],
|
||||
"created": record["created"],
|
||||
"updated": record["updated"],
|
||||
}
|
||||
|
||||
|
||||
def write_index(records: list[dict[str, Any]]) -> None:
|
||||
by_dir: dict[str, list[dict[str, Any]]] = {}
|
||||
for record in records:
|
||||
directory = str(Path(record["path"]).parent)
|
||||
if directory == ".":
|
||||
directory = "."
|
||||
by_dir.setdefault(directory, []).append(record)
|
||||
|
||||
total = len(records)
|
||||
lines = [
|
||||
"---",
|
||||
f"generated: {date.today().isoformat()}",
|
||||
f"total: {total}",
|
||||
"---",
|
||||
"",
|
||||
"# Vault Index",
|
||||
"",
|
||||
"## Статистика",
|
||||
"",
|
||||
"| Параметр | Значение |",
|
||||
"|----------|----------|",
|
||||
f"| Всего заметок | {total} |",
|
||||
"",
|
||||
]
|
||||
|
||||
for directory in sorted(by_dir, key=str.lower):
|
||||
lines.extend(
|
||||
[
|
||||
f"## {directory}",
|
||||
"",
|
||||
"| Заметка | Aliases | Status | Type | Tags |",
|
||||
"|---------|---------|--------|------|------|",
|
||||
]
|
||||
)
|
||||
for record in sorted(by_dir[directory], key=lambda item: item["name"].lower()):
|
||||
aliases = ", ".join(record["aliases"]) if record["aliases"] else "—"
|
||||
status = record["status"] or "—"
|
||||
note_type = record["type"] or "—"
|
||||
tags = "[" + ", ".join(record["tags"]) + "]"
|
||||
lines.append(f"| [[{record['name']}]] | {aliases} | {status} | {note_type} | {tags} |")
|
||||
lines.append("")
|
||||
|
||||
INDEX_PATH.write_text("\n".join(lines), encoding="utf-8")
|
||||
|
||||
|
||||
def source_values(value: Any) -> list[str]:
|
||||
if isinstance(value, list):
|
||||
return [clean_scalar(item) for item in value if clean_scalar(item)]
|
||||
if value:
|
||||
return [clean_scalar(value)]
|
||||
return []
|
||||
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from datetime import date
|
||||
from pathlib import Path
|
||||
|
||||
from vault_common import (
|
||||
REQUIRED_FRONTMATTER,
|
||||
ROOT,
|
||||
VALID_STATUS,
|
||||
VALID_TYPE,
|
||||
all_records,
|
||||
as_list,
|
||||
json_print,
|
||||
note_record,
|
||||
note_files,
|
||||
rel,
|
||||
split_frontmatter,
|
||||
)
|
||||
|
||||
|
||||
def resolve_paths(paths: list[str]) -> list[Path]:
|
||||
if not paths:
|
||||
return note_files()
|
||||
result = []
|
||||
for item in paths:
|
||||
path = Path(item)
|
||||
if not path.is_absolute():
|
||||
path = ROOT / path
|
||||
result.append(path)
|
||||
return result
|
||||
|
||||
|
||||
def validate_record(record: dict[str, object]) -> list[str]:
|
||||
fm = record["frontmatter"]
|
||||
issues = []
|
||||
|
||||
if not record["has_frontmatter"]:
|
||||
issues.append("missing_frontmatter")
|
||||
|
||||
for field in sorted(REQUIRED_FRONTMATTER):
|
||||
if field not in fm or fm[field] in (None, ""):
|
||||
issues.append(f"missing_{field}")
|
||||
|
||||
if fm.get("status") and str(fm["status"]) not in VALID_STATUS:
|
||||
issues.append("invalid_status")
|
||||
if fm.get("type") and str(fm["type"]) not in VALID_TYPE:
|
||||
issues.append("invalid_type")
|
||||
if "tags" in fm and not isinstance(fm["tags"], list):
|
||||
issues.append("tags_not_list")
|
||||
if "aliases" in fm and fm["aliases"] not in (None, "") and not isinstance(fm["aliases"], list):
|
||||
issues.append("aliases_not_list")
|
||||
|
||||
return issues
|
||||
|
||||
|
||||
def check(paths: list[Path]) -> list[dict[str, object]]:
|
||||
results = []
|
||||
for path in paths:
|
||||
record = note_record(path)
|
||||
issues = validate_record(record)
|
||||
if issues:
|
||||
results.append({"path": rel(path), "issues": issues})
|
||||
return results
|
||||
|
||||
|
||||
def yaml_list(values: list[str]) -> list[str]:
|
||||
if not values:
|
||||
return ["[]"]
|
||||
return [""] + [f" - {value}" for value in values]
|
||||
|
||||
|
||||
def normalize(path: Path) -> dict[str, object]:
|
||||
text = path.read_text(encoding="utf-8")
|
||||
fm, body, _ = split_frontmatter(text)
|
||||
title = fm.get("title") or path.stem
|
||||
status = fm.get("status") if fm.get("status") in VALID_STATUS else "seed"
|
||||
note_type = fm.get("type") if fm.get("type") in VALID_TYPE else "concept"
|
||||
tags = as_list(fm.get("tags"))
|
||||
aliases = as_list(fm.get("aliases"))
|
||||
created = fm.get("created") or date.today().isoformat()
|
||||
updated = date.today().isoformat()
|
||||
|
||||
lines = [
|
||||
"---",
|
||||
f"title: {title}",
|
||||
f"status: {status}",
|
||||
f"type: {note_type}",
|
||||
"tags:" + ("\n".join(yaml_list(tags)) if tags else " []"),
|
||||
f"created: {created}",
|
||||
f"updated: {updated}",
|
||||
"aliases:" + ("\n".join(yaml_list(aliases)) if aliases else " []"),
|
||||
]
|
||||
|
||||
for key, value in fm.items():
|
||||
if key in {"title", "status", "type", "tags", "created", "updated", "aliases"}:
|
||||
continue
|
||||
if isinstance(value, list):
|
||||
lines.append(f"{key}:")
|
||||
lines.extend(f" - {item}" for item in value)
|
||||
elif value in (None, ""):
|
||||
lines.append(f"{key}:")
|
||||
else:
|
||||
lines.append(f"{key}: {value}")
|
||||
|
||||
lines.extend(["---", "", body.lstrip("\n")])
|
||||
path.write_text("\n".join(lines), encoding="utf-8")
|
||||
return {"path": rel(path), "written": True}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Agent-facing frontmatter helper")
|
||||
sub = parser.add_subparsers(dest="command", required=True)
|
||||
|
||||
check_cmd = sub.add_parser("check")
|
||||
check_cmd.add_argument("paths", nargs="*")
|
||||
|
||||
normalize_cmd = sub.add_parser("normalize")
|
||||
normalize_cmd.add_argument("paths", nargs="+")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.command == "check":
|
||||
issues = check(resolve_paths(args.paths))
|
||||
json_print({"ok": True, "issues": issues, "total_with_issues": len(issues)})
|
||||
elif args.command == "normalize":
|
||||
written = [normalize(path) for path in resolve_paths(args.paths)]
|
||||
json_print({"ok": True, "written": written})
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||