This commit is contained in:
1
.vscode/extensions.json
vendored
1
.vscode/extensions.json
vendored
@@ -28,7 +28,6 @@
|
|||||||
"vivaxy.vscode-conventional-commits",
|
"vivaxy.vscode-conventional-commits",
|
||||||
"christian-kohler.path-intellisense",
|
"christian-kohler.path-intellisense",
|
||||||
"christian-kohler.npm-intellisense",
|
"christian-kohler.npm-intellisense",
|
||||||
"chakrounanas.turbo-console-log",
|
|
||||||
"pranaygp.vscode-css-peek",
|
"pranaygp.vscode-css-peek",
|
||||||
"alefragnani.bookmarks",
|
"alefragnani.bookmarks",
|
||||||
"pkief.material-icon-theme",
|
"pkief.material-icon-theme",
|
||||||
|
|||||||
BIN
LCBP3_20260221.bin
Normal file
BIN
LCBP3_20260221.bin
Normal file
Binary file not shown.
@@ -4,7 +4,7 @@
|
|||||||
{ "name": "🔧 Backend", "path": "./backend" },
|
{ "name": "🔧 Backend", "path": "./backend" },
|
||||||
{ "name": "🎨 Frontend", "path": "./frontend" },
|
{ "name": "🎨 Frontend", "path": "./frontend" },
|
||||||
{ "name": "🗓️ docs", "path": "./docs" },
|
{ "name": "🗓️ docs", "path": "./docs" },
|
||||||
{ "name": "🔗 specs", "path": "./specs" }
|
{ "name": "🔗 specs", "path": "./specs" },
|
||||||
],
|
],
|
||||||
"settings": {
|
"settings": {
|
||||||
// ========================================
|
// ========================================
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"editor.smoothScrolling": true,
|
"editor.smoothScrolling": true,
|
||||||
"editor.cursorBlinking": "smooth",
|
"editor.cursorBlinking": "smooth",
|
||||||
"editor.cursorSmoothCaretAnimation": "on",
|
"editor.cursorSmoothCaretAnimation": "on",
|
||||||
"editor.wordWrap": "on",
|
"editor.wordWrap": "off",
|
||||||
"editor.linkedEditing": true,
|
"editor.linkedEditing": true,
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"editor.formatOnPaste": true,
|
"editor.formatOnPaste": true,
|
||||||
@@ -39,41 +39,41 @@
|
|||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
|
||||||
"[javascript]": {
|
"[javascript]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
},
|
},
|
||||||
"[javascriptreact]": {
|
"[javascriptreact]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
},
|
},
|
||||||
"[typescript]": {
|
"[typescript]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
},
|
},
|
||||||
"[typescriptreact]": {
|
"[typescriptreact]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
},
|
},
|
||||||
"[json]": {
|
"[json]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
},
|
},
|
||||||
"[jsonc]": {
|
"[jsonc]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
},
|
},
|
||||||
"[html]": {
|
"[html]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
},
|
},
|
||||||
"[css]": {
|
"[css]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
},
|
},
|
||||||
"[scss]": {
|
"[scss]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
},
|
},
|
||||||
"[markdown]": {
|
"[markdown]": {
|
||||||
"editor.defaultFormatter": "yzhang.markdown-all-in-one",
|
"editor.defaultFormatter": "yzhang.markdown-all-in-one",
|
||||||
"editor.wordWrap": "on"
|
"editor.wordWrap": "on",
|
||||||
},
|
},
|
||||||
"[yaml]": {
|
"[yaml]": {
|
||||||
"editor.defaultFormatter": "redhat.vscode-yaml"
|
"editor.defaultFormatter": "redhat.vscode-yaml",
|
||||||
},
|
},
|
||||||
"[dockerfile]": {
|
"[dockerfile]": {
|
||||||
"editor.defaultFormatter": "ms-azuretools.vscode-containers"
|
"editor.defaultFormatter": "ms-azuretools.vscode-containers",
|
||||||
},
|
},
|
||||||
"[sql]": {
|
"[sql]": {
|
||||||
"editor.defaultFormatter": "mtxr.sqltools",
|
"editor.defaultFormatter": "mtxr.sqltools",
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
"editor.insertSpaces": true,
|
"editor.insertSpaces": true,
|
||||||
"editor.detectIndentation": false,
|
"editor.detectIndentation": false,
|
||||||
"editor.wordWrap": "off",
|
"editor.wordWrap": "off",
|
||||||
"editor.formatOnSave": true
|
"editor.formatOnSave": true,
|
||||||
},
|
},
|
||||||
"sqltools.format": {
|
"sqltools.format": {
|
||||||
"indent": " ", // 2 spaces
|
"indent": " ", // 2 spaces
|
||||||
@@ -92,7 +92,7 @@
|
|||||||
"functionCase": "lower", // count(), sum(), date_format()
|
"functionCase": "lower", // count(), sum(), date_format()
|
||||||
// Spacing and Lines
|
// Spacing and Lines
|
||||||
"linesBetweenQueries": 2, // เว้นบรรทัดระหว่าง query
|
"linesBetweenQueries": 2, // เว้นบรรทัดระหว่าง query
|
||||||
"denseOperators": true,
|
//"denseOperators": true,
|
||||||
"spaceAroundOperators": false,
|
"spaceAroundOperators": false,
|
||||||
// Comma Style
|
// Comma Style
|
||||||
"commaPosition": "after", // ใส่ comma หลังคอลัมน์
|
"commaPosition": "after", // ใส่ comma หลังคอลัมน์
|
||||||
@@ -109,7 +109,7 @@
|
|||||||
// Other Styles
|
// Other Styles
|
||||||
"compact": true, // ไม่ย่อโค้ดให้แน่นเกินไป
|
"compact": true, // ไม่ย่อโค้ดให้แน่นเกินไป
|
||||||
"uppercaseKeywords": true,
|
"uppercaseKeywords": true,
|
||||||
"newlineBeforeSemicolon": false
|
"newlineBeforeSemicolon": false,
|
||||||
},
|
},
|
||||||
// ป้องกัน extension อื่นมายุ่ง
|
// ป้องกัน extension อื่นมายุ่ง
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@
|
|||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll": "explicit",
|
"source.fixAll": "explicit",
|
||||||
"source.fixAll.prettier": "explicit",
|
"source.fixAll.prettier": "explicit",
|
||||||
"source.fixAll.eslint": "explicit"
|
"source.fixAll.eslint": "explicit",
|
||||||
//"source.organizeImports": "explicit",
|
//"source.organizeImports": "explicit",
|
||||||
//"source.addMissingImports": "explicit"
|
//"source.addMissingImports": "explicit"
|
||||||
},
|
},
|
||||||
@@ -178,7 +178,7 @@
|
|||||||
"@public": "${workspaceFolder:🎨 Frontend}/public",
|
"@public": "${workspaceFolder:🎨 Frontend}/public",
|
||||||
"@styles": "${workspaceFolder:🎨 Frontend}/styles",
|
"@styles": "${workspaceFolder:🎨 Frontend}/styles",
|
||||||
"@types": "${workspaceFolder:🎨 Frontend}/types",
|
"@types": "${workspaceFolder:🎨 Frontend}/types",
|
||||||
"@api": "${workspaceFolder:🎨 Frontend}/app/api"
|
"@api": "${workspaceFolder:🎨 Frontend}/app/api",
|
||||||
},
|
},
|
||||||
"path-intellisense.autoSlashAfterDirectory": true,
|
"path-intellisense.autoSlashAfterDirectory": true,
|
||||||
"path-intellisense.extensionOnImport": false,
|
"path-intellisense.extensionOnImport": false,
|
||||||
@@ -203,18 +203,18 @@
|
|||||||
"tailwindCSS.suggestions": true,
|
"tailwindCSS.suggestions": true,
|
||||||
"tailwindCSS.includeLanguages": {
|
"tailwindCSS.includeLanguages": {
|
||||||
"typescript": "javascript",
|
"typescript": "javascript",
|
||||||
"typescriptreact": "javascript"
|
"typescriptreact": "javascript",
|
||||||
},
|
},
|
||||||
"tailwindCSS.experimental.classRegex": [
|
"tailwindCSS.experimental.classRegex": [
|
||||||
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
|
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
|
||||||
["cn\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
|
["cn\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
|
||||||
"class[Nn]ame\\s*=\\s*['\"`]([^'\"`]*)['\"`]"
|
"class[Nn]ame\\s*=\\s*['\"`]([^'\"`]*)['\"`]",
|
||||||
],
|
],
|
||||||
|
|
||||||
// ระบุที่ตั้งของ tailwind.config (ถ้ามี)
|
// ระบุที่ตั้งของ tailwind.config (ถ้ามี)
|
||||||
"tailwindCSS.experimental.configFile": {
|
"tailwindCSS.experimental.configFile": {
|
||||||
//"backend/tailwind.config.js": "backend/**",
|
//"backend/tailwind.config.js": "backend/**",
|
||||||
"frontend/tailwind.config.ts": "frontend/**"
|
"frontend/tailwind.config.ts": "frontend/**",
|
||||||
},
|
},
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
@@ -233,7 +233,7 @@
|
|||||||
"javascript",
|
"javascript",
|
||||||
"javascriptreact",
|
"javascriptreact",
|
||||||
"typescript",
|
"typescript",
|
||||||
"typescriptreact"
|
"typescriptreact",
|
||||||
],
|
],
|
||||||
|
|
||||||
"auto-rename-tag.activationOnLanguage": [
|
"auto-rename-tag.activationOnLanguage": [
|
||||||
@@ -242,7 +242,7 @@
|
|||||||
"javascript",
|
"javascript",
|
||||||
"javascriptreact",
|
"javascriptreact",
|
||||||
"typescript",
|
"typescript",
|
||||||
"typescriptreact"
|
"typescriptreact",
|
||||||
],
|
],
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
@@ -255,32 +255,32 @@
|
|||||||
"color": "#FF2D00",
|
"color": "#FF2D00",
|
||||||
"strikethrough": false,
|
"strikethrough": false,
|
||||||
"backgroundColor": "transparent",
|
"backgroundColor": "transparent",
|
||||||
"bold": true
|
"bold": true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tag": "?",
|
"tag": "?",
|
||||||
"color": "#3498DB",
|
"color": "#3498DB",
|
||||||
"strikethrough": false,
|
"strikethrough": false,
|
||||||
"backgroundColor": "transparent"
|
"backgroundColor": "transparent",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tag": "//",
|
"tag": "//",
|
||||||
"color": "#474747",
|
"color": "#474747",
|
||||||
"strikethrough": true,
|
"strikethrough": true,
|
||||||
"backgroundColor": "transparent"
|
"backgroundColor": "transparent",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tag": "todo",
|
"tag": "todo",
|
||||||
"color": "#FF8C00",
|
"color": "#FF8C00",
|
||||||
"strikethrough": false,
|
"strikethrough": false,
|
||||||
"backgroundColor": "transparent"
|
"backgroundColor": "transparent",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tag": "*",
|
"tag": "*",
|
||||||
"color": "#98C379",
|
"color": "#98C379",
|
||||||
"strikethrough": false,
|
"strikethrough": false,
|
||||||
"backgroundColor": "transparent"
|
"backgroundColor": "transparent",
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
@@ -295,28 +295,28 @@
|
|||||||
"TODO": {
|
"TODO": {
|
||||||
"icon": "check",
|
"icon": "check",
|
||||||
"iconColour": "#FF8C00",
|
"iconColour": "#FF8C00",
|
||||||
"foreground": "#FF8C00"
|
"foreground": "#FF8C00",
|
||||||
},
|
},
|
||||||
"FIXME": {
|
"FIXME": {
|
||||||
"icon": "alert",
|
"icon": "alert",
|
||||||
"iconColour": "#FF2D00",
|
"iconColour": "#FF2D00",
|
||||||
"foreground": "#FF2D00"
|
"foreground": "#FF2D00",
|
||||||
},
|
},
|
||||||
"BUG": {
|
"BUG": {
|
||||||
"icon": "bug",
|
"icon": "bug",
|
||||||
"iconColour": "#FF2D00",
|
"iconColour": "#FF2D00",
|
||||||
"foreground": "#FF2D00"
|
"foreground": "#FF2D00",
|
||||||
},
|
},
|
||||||
"NOTE": {
|
"NOTE": {
|
||||||
"icon": "note",
|
"icon": "note",
|
||||||
"iconColour": "#3498DB",
|
"iconColour": "#3498DB",
|
||||||
"foreground": "#3498DB"
|
"foreground": "#3498DB",
|
||||||
},
|
},
|
||||||
"HACK": {
|
"HACK": {
|
||||||
"icon": "tools",
|
"icon": "tools",
|
||||||
"iconColour": "#FFA500",
|
"iconColour": "#FFA500",
|
||||||
"foreground": "#FFA500"
|
"foreground": "#FFA500",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
@@ -335,6 +335,7 @@
|
|||||||
"gitlens.views.repositories.location": "scm",
|
"gitlens.views.repositories.location": "scm",
|
||||||
"gitlens.views.fileHistory.location": "explorer",
|
"gitlens.views.fileHistory.location": "explorer",
|
||||||
"gitlens.views.lineHistory.location": "explorer",
|
"gitlens.views.lineHistory.location": "explorer",
|
||||||
|
"gitlens.terminal.enabled": false,
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// GIT
|
// GIT
|
||||||
@@ -369,7 +370,7 @@
|
|||||||
"javascript.updateImportsOnFileMove.enabled": "always",
|
"javascript.updateImportsOnFileMove.enabled": "always",
|
||||||
"javascript.inlayHints.parameterNames.enabled": "all",
|
"javascript.inlayHints.parameterNames.enabled": "all",
|
||||||
"javascript.inlayHints.functionLikeReturnTypes.enabled": true,
|
"javascript.inlayHints.functionLikeReturnTypes.enabled": true,
|
||||||
"javascript.inlayHints.variableTypes.enabled": true,
|
"javascript.inlayHints.variableTypes.enabled": false,
|
||||||
"javascript.preferences.importModuleSpecifier": "relative",
|
"javascript.preferences.importModuleSpecifier": "relative",
|
||||||
|
|
||||||
"typescript.suggest.autoImports": true,
|
"typescript.suggest.autoImports": true,
|
||||||
@@ -386,7 +387,7 @@
|
|||||||
|
|
||||||
"emmet.includeLanguages": {
|
"emmet.includeLanguages": {
|
||||||
"javascript": "javascriptreact",
|
"javascript": "javascriptreact",
|
||||||
"typescript": "typescriptreact"
|
"typescript": "typescriptreact",
|
||||||
},
|
},
|
||||||
"emmet.triggerExpansionOnTab": true,
|
"emmet.triggerExpansionOnTab": true,
|
||||||
"emmet.showSuggestionsAsSnippets": true,
|
"emmet.showSuggestionsAsSnippets": true,
|
||||||
@@ -404,7 +405,7 @@
|
|||||||
"files.associations": {
|
"files.associations": {
|
||||||
"*.css": "tailwindcss",
|
"*.css": "tailwindcss",
|
||||||
".env*": "dotenv",
|
".env*": "dotenv",
|
||||||
"*.md": "markdown"
|
"*.md": "markdown",
|
||||||
},
|
},
|
||||||
|
|
||||||
"files.exclude": {
|
"files.exclude": {
|
||||||
@@ -417,7 +418,7 @@
|
|||||||
"**/.turbo": true,
|
"**/.turbo": true,
|
||||||
"**/coverage": true,
|
"**/coverage": true,
|
||||||
"**/.nyc_output": true,
|
"**/.nyc_output": true,
|
||||||
"**/*.log": true
|
"**/*.log": true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"files.watcherExclude": {
|
"files.watcherExclude": {
|
||||||
@@ -427,7 +428,7 @@
|
|||||||
"**/.next/**": true,
|
"**/.next/**": true,
|
||||||
"**/dist/**": true,
|
"**/dist/**": true,
|
||||||
"**/build/**": true,
|
"**/build/**": true,
|
||||||
"**/.turbo/**": true
|
"**/.turbo/**": true,
|
||||||
},
|
},
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
@@ -445,7 +446,7 @@
|
|||||||
"**/yarn.lock": true,
|
"**/yarn.lock": true,
|
||||||
"**/package-lock.json": true,
|
"**/package-lock.json": true,
|
||||||
"**/pnpm-lock.yaml": true,
|
"**/pnpm-lock.yaml": true,
|
||||||
"**/*.log": true
|
"**/*.log": true,
|
||||||
},
|
},
|
||||||
"search.followSymlinks": false,
|
"search.followSymlinks": false,
|
||||||
"search.useIgnoreFiles": true,
|
"search.useIgnoreFiles": true,
|
||||||
@@ -466,8 +467,22 @@
|
|||||||
"terminal.integrated.profiles.windows": {
|
"terminal.integrated.profiles.windows": {
|
||||||
"PowerShell-7": {
|
"PowerShell-7": {
|
||||||
"path": "C:\\Program Files\\PowerShell\\7\\pwsh.exe",
|
"path": "C:\\Program Files\\PowerShell\\7\\pwsh.exe",
|
||||||
"icon": "terminal-powershell"
|
"icon": "terminal-powershell",
|
||||||
}
|
},
|
||||||
|
"SSH QNAP": {
|
||||||
|
"path": "C:\\Windows\\System32\\OpenSSH\\ssh.exe",
|
||||||
|
"args": ["qnap"],
|
||||||
|
"icon": "terminal-linux",
|
||||||
|
"color": "terminal.ansiGreen",
|
||||||
|
"overrideName": true,
|
||||||
|
},
|
||||||
|
"SSH ASUSTOR": {
|
||||||
|
"path": "C:\\Windows\\System32\\OpenSSH\\ssh.exe",
|
||||||
|
"args": ["asustor"],
|
||||||
|
"icon": "terminal-linux",
|
||||||
|
"color": "terminal.ansiBlue",
|
||||||
|
"overrideName": true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
@@ -485,7 +500,7 @@
|
|||||||
"workbench.editor.limit.value": 10,
|
"workbench.editor.limit.value": 10,
|
||||||
"workbench.startupEditor": "welcomePage",
|
"workbench.startupEditor": "welcomePage",
|
||||||
"workbench.view.showQuietly": {
|
"workbench.view.showQuietly": {
|
||||||
"workbench.panel.output": false
|
"workbench.panel.output": false,
|
||||||
},
|
},
|
||||||
// ========================================
|
// ========================================
|
||||||
// EXPLORER
|
// EXPLORER
|
||||||
@@ -505,7 +520,7 @@
|
|||||||
"*.ts": "${capture}.test.ts, ${capture}.spec.ts",
|
"*.ts": "${capture}.test.ts, ${capture}.spec.ts",
|
||||||
"*.tsx": "${capture}.test.tsx, ${capture}.spec.tsx, ${capture}.module.css",
|
"*.tsx": "${capture}.test.tsx, ${capture}.spec.tsx, ${capture}.module.css",
|
||||||
"*.js": "${capture}.test.js, ${capture}.spec.js",
|
"*.js": "${capture}.test.js, ${capture}.spec.js",
|
||||||
"*.jsx": "${capture}.test.jsx, ${capture}.spec.jsx"
|
"*.jsx": "${capture}.test.jsx, ${capture}.spec.jsx",
|
||||||
},
|
},
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
@@ -555,10 +570,8 @@
|
|||||||
// ========================================
|
// ========================================
|
||||||
|
|
||||||
"yaml.schemas": {
|
"yaml.schemas": {
|
||||||
"https://json.schemastore.org/github-workflow.json": ".github/workflows/*.{yml,yaml}",
|
|
||||||
"https://json.schemastore.org/github-action.json": "action.{yml,yaml}",
|
|
||||||
"https://json.schemastore.org/prettierrc.json": ".prettierrc.{yml,yaml}",
|
"https://json.schemastore.org/prettierrc.json": ".prettierrc.{yml,yaml}",
|
||||||
"https://json.schemastore.org/docker-compose.json": "docker-compose*.{yml,yaml}"
|
"https://json.schemastore.org/docker-compose.json": "docker-compose*.{yml,yaml}",
|
||||||
},
|
},
|
||||||
"yaml.format.enable": true,
|
"yaml.format.enable": true,
|
||||||
"yaml.format.singleQuote": false,
|
"yaml.format.singleQuote": false,
|
||||||
@@ -575,14 +588,14 @@
|
|||||||
"rest-client.showResponseInDifferentTab": true,
|
"rest-client.showResponseInDifferentTab": true,
|
||||||
"rest-client.environmentVariables": {
|
"rest-client.environmentVariables": {
|
||||||
"$shared": {
|
"$shared": {
|
||||||
"apiUrl": "http://localhost:3000"
|
"apiUrl": "http://localhost:3000",
|
||||||
},
|
},
|
||||||
"development": {
|
"development": {
|
||||||
"apiUrl": "http://localhost:3000"
|
"apiUrl": "http://localhost:3000",
|
||||||
},
|
},
|
||||||
"production": {
|
"production": {
|
||||||
"apiUrl": "https://lcbp3.nap-dms.work"
|
"apiUrl": "https://lcbp3.nap-dms.work",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
@@ -595,7 +608,7 @@
|
|||||||
"*.env.local": "Tune",
|
"*.env.local": "Tune",
|
||||||
"*.env.development": "Tune",
|
"*.env.development": "Tune",
|
||||||
"*.env.production": "Tune",
|
"*.env.production": "Tune",
|
||||||
"docker-compose.*.yml": "Docker"
|
"docker-compose.*.yml": "Docker",
|
||||||
},
|
},
|
||||||
"material-icon-theme.folders.associations": {
|
"material-icon-theme.folders.associations": {
|
||||||
"hooks": "Custom",
|
"hooks": "Custom",
|
||||||
@@ -607,7 +620,7 @@
|
|||||||
"entities": "Database",
|
"entities": "Database",
|
||||||
"modules": "Folder-Controllers",
|
"modules": "Folder-Controllers",
|
||||||
"common": "Shared",
|
"common": "Shared",
|
||||||
"config": "Config"
|
"config": "Config",
|
||||||
},
|
},
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
@@ -623,7 +636,7 @@
|
|||||||
// PERFORMANCE
|
// PERFORMANCE
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
||||||
"files.maxMemoryForLargeFilesMB": 4096,
|
"files.maxMemoryForLargeFilesMB": 1024,
|
||||||
"telemetry.telemetryLevel": "off",
|
"telemetry.telemetryLevel": "off",
|
||||||
"security.workspace.trust.untrustedFiles": "open",
|
"security.workspace.trust.untrustedFiles": "open",
|
||||||
"extensions.ignoreRecommendations": false,
|
"extensions.ignoreRecommendations": false,
|
||||||
@@ -643,7 +656,7 @@
|
|||||||
{
|
{
|
||||||
"mysqlOptions": {
|
"mysqlOptions": {
|
||||||
"authProtocol": "default",
|
"authProtocol": "default",
|
||||||
"enableSsl": "Disabled"
|
"enableSsl": "Disabled",
|
||||||
},
|
},
|
||||||
"ssh": "Disabled",
|
"ssh": "Disabled",
|
||||||
"previewLimit": 50,
|
"previewLimit": 50,
|
||||||
@@ -654,8 +667,8 @@
|
|||||||
"database": "lcbp3_dev",
|
"database": "lcbp3_dev",
|
||||||
"username": "root",
|
"username": "root",
|
||||||
"password": "",
|
"password": "",
|
||||||
"askForPassword": true // ✅ ปลอดภัยกว่า
|
"askForPassword": true, // ✅ ปลอดภัยกว่า
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
"database-client.variableIndicator": [":", "$"],
|
"database-client.variableIndicator": [":", "$"],
|
||||||
"geminicodeassist.rules": "ใช้ภาษาไทยในการโต้ตอบ\n\n\n\n",
|
"geminicodeassist.rules": "ใช้ภาษาไทยในการโต้ตอบ\n\n\n\n",
|
||||||
@@ -664,7 +677,7 @@
|
|||||||
"vitest.commandLine": "npm run test --",
|
"vitest.commandLine": "npm run test --",
|
||||||
"vitest.enable": true,
|
"vitest.enable": true,
|
||||||
"yaml.maxItemsComputed": 6000,
|
"yaml.maxItemsComputed": 6000,
|
||||||
"powershell.cwd": "🎯 Root"
|
"powershell.cwd": "🎯 Root",
|
||||||
},
|
},
|
||||||
// ========================================
|
// ========================================
|
||||||
// LAUNCH CONFIGURATIONS
|
// LAUNCH CONFIGURATIONS
|
||||||
@@ -683,7 +696,7 @@
|
|||||||
// "internalConsoleOptions": "neverOpen",
|
// "internalConsoleOptions": "neverOpen",
|
||||||
"skipFiles": ["<node_internals>/**"],
|
"skipFiles": ["<node_internals>/**"],
|
||||||
"sourceMaps": true,
|
"sourceMaps": true,
|
||||||
"restart": true
|
"restart": true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "🎨 Debug Frontend (Next.js)",
|
"name": "🎨 Debug Frontend (Next.js)",
|
||||||
@@ -695,8 +708,8 @@
|
|||||||
"serverReadyAction": {
|
"serverReadyAction": {
|
||||||
"pattern": "- Local:.+(https?://\\S+)",
|
"pattern": "- Local:.+(https?://\\S+)",
|
||||||
"uriFormat": "%s",
|
"uriFormat": "%s",
|
||||||
"action": "debugWithChrome"
|
"action": "debugWithChrome",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "🧪 Debug Backend Tests (Jest)",
|
"name": "🧪 Debug Backend Tests (Jest)",
|
||||||
@@ -705,7 +718,7 @@
|
|||||||
"runtimeExecutable": "npm",
|
"runtimeExecutable": "npm",
|
||||||
"runtimeArgs": ["run", "test:debug"],
|
"runtimeArgs": ["run", "test:debug"],
|
||||||
"cwd": "${workspaceFolder:🔧 Backend}",
|
"cwd": "${workspaceFolder:🔧 Backend}",
|
||||||
"console": "integratedTerminal"
|
"console": "integratedTerminal",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "🧪 Debug Frontend Tests (Vitest)",
|
"name": "🧪 Debug Frontend Tests (Vitest)",
|
||||||
@@ -715,17 +728,17 @@
|
|||||||
"runtimeArgs": ["run", "test:debug"],
|
"runtimeArgs": ["run", "test:debug"],
|
||||||
"cwd": "${workspaceFolder:🎨 Frontend}",
|
"cwd": "${workspaceFolder:🎨 Frontend}",
|
||||||
"console": "integratedTerminal",
|
"console": "integratedTerminal",
|
||||||
"internalConsoleOptions": "neverOpen"
|
"internalConsoleOptions": "neverOpen",
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
"compounds": [
|
"compounds": [
|
||||||
{
|
{
|
||||||
"name": "🚀 Debug Full Stack",
|
"name": "🚀 Debug Full Stack",
|
||||||
"configurations": ["🔧 Debug Backend (NestJS)", "🎨 Debug Frontend (Next.js)"],
|
"configurations": ["🔧 Debug Backend (NestJS)", "🎨 Debug Frontend (Next.js)"],
|
||||||
"stopAll": true
|
"stopAll": true,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
// ========================================
|
// ========================================
|
||||||
// TASKS
|
// TASKS
|
||||||
@@ -738,130 +751,166 @@
|
|||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "npm run start:dev",
|
"command": "npm run start:dev",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder:🔧 Backend}"
|
"cwd": "${workspaceFolder:🔧 Backend}",
|
||||||
},
|
},
|
||||||
"problemMatcher": [],
|
"problemMatcher": [],
|
||||||
"isBackground": true,
|
"isBackground": true,
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"reveal": "always",
|
"reveal": "always",
|
||||||
"panel": "dedicated",
|
"panel": "dedicated",
|
||||||
"group": "dev"
|
"group": "dev",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "🎨 Start Frontend Dev",
|
"label": "🎨 Start Frontend Dev",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "npm run dev",
|
"command": "npm run dev",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder:🎨 Frontend}"
|
"cwd": "${workspaceFolder:🎨 Frontend}",
|
||||||
},
|
},
|
||||||
"problemMatcher": [],
|
"problemMatcher": [],
|
||||||
"isBackground": true,
|
"isBackground": true,
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"reveal": "always",
|
"reveal": "always",
|
||||||
"panel": "dedicated",
|
"panel": "dedicated",
|
||||||
"group": "dev"
|
"group": "dev",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "🚀 Start Full Stack",
|
"label": "🚀 Start Full Stack",
|
||||||
"dependsOn": ["🔧 Start Backend Dev", "🎨 Start Frontend Dev"],
|
"dependsOn": ["🔧 Start Backend Dev", "🎨 Start Frontend Dev"],
|
||||||
"problemMatcher": []
|
"problemMatcher": [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "🧪 Run Backend Tests",
|
"label": "🧪 Run Backend Tests",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "npm run test",
|
"command": "npm run test",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder:🔧 Backend}"
|
"cwd": "${workspaceFolder:🔧 Backend}",
|
||||||
},
|
},
|
||||||
"problemMatcher": []
|
"problemMatcher": [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "🧪 Run Frontend Tests",
|
"label": "🧪 Run Frontend Tests",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "npm run test",
|
"command": "npm run test",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder:🎨 Frontend}"
|
"cwd": "${workspaceFolder:🎨 Frontend}",
|
||||||
},
|
},
|
||||||
"problemMatcher": []
|
"problemMatcher": [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "🧪 Watch Backend Tests",
|
"label": "🧪 Watch Backend Tests",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "npm run test:watch",
|
"command": "npm run test:watch",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder:🔧 Backend}"
|
"cwd": "${workspaceFolder:🔧 Backend}",
|
||||||
},
|
},
|
||||||
"problemMatcher": [],
|
"problemMatcher": [],
|
||||||
"isBackground": true
|
"isBackground": true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "🧪 Watch Frontend Tests",
|
"label": "🧪 Watch Frontend Tests",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "npm run test:watch",
|
"command": "npm run test:watch",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder:🎨 Frontend}"
|
"cwd": "${workspaceFolder:🎨 Frontend}",
|
||||||
},
|
},
|
||||||
"problemMatcher": [],
|
"problemMatcher": [],
|
||||||
"isBackground": true
|
"isBackground": true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "🏗️ Build Backend",
|
"label": "🏗️ Build Backend",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "npm run build",
|
"command": "npm run build",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder:🔧 Backend}"
|
"cwd": "${workspaceFolder:🔧 Backend}",
|
||||||
},
|
},
|
||||||
"problemMatcher": ["$tsc"],
|
"problemMatcher": ["$tsc"],
|
||||||
"group": {
|
"group": {
|
||||||
"kind": "build",
|
"kind": "build",
|
||||||
"isDefault": false
|
"isDefault": false,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "🏗️ Build Frontend",
|
"label": "🏗️ Build Frontend",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "npm run build",
|
"command": "npm run build",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder:🎨 Frontend}"
|
"cwd": "${workspaceFolder:🎨 Frontend}",
|
||||||
},
|
},
|
||||||
"problemMatcher": ["$tsc"],
|
"problemMatcher": ["$tsc"],
|
||||||
"group": {
|
"group": {
|
||||||
"kind": "build",
|
"kind": "build",
|
||||||
"isDefault": false
|
"isDefault": false,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "🔍 Lint Backend",
|
"label": "🔍 Lint Backend",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "npm run lint",
|
"command": "npm run lint",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder:🔧 Backend}"
|
"cwd": "${workspaceFolder:🔧 Backend}",
|
||||||
},
|
},
|
||||||
"problemMatcher": ["$eslint-stylish"]
|
"problemMatcher": ["$eslint-stylish"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "🔍 Lint Frontend",
|
"label": "🔍 Lint Frontend",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "npm run lint",
|
"command": "npm run lint",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder:🎨 Frontend}"
|
"cwd": "${workspaceFolder:🎨 Frontend}",
|
||||||
},
|
},
|
||||||
"problemMatcher": ["$eslint-stylish"]
|
"problemMatcher": ["$eslint-stylish"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "🐳 Docker Compose Up",
|
"label": "🐳 Docker Compose Up",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "docker-compose up -d",
|
"command": "docker-compose up -d",
|
||||||
"problemMatcher": []
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder:🎯 Root}",
|
||||||
|
},
|
||||||
|
"problemMatcher": [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "🐳 Docker Compose Down",
|
"label": "🐳 Docker Compose Down",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "docker-compose down",
|
"command": "docker-compose down",
|
||||||
"problemMatcher": []
|
"options": {
|
||||||
}
|
"cwd": "${workspaceFolder:🎯 Root}",
|
||||||
]
|
},
|
||||||
}
|
"problemMatcher": [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "🖥️ SSH QNAP",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "ssh qnap",
|
||||||
|
"isBackground": true,
|
||||||
|
"problemMatcher": [],
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "always",
|
||||||
|
"panel": "dedicated",
|
||||||
|
"group": "ssh",
|
||||||
|
},
|
||||||
|
"runOptions": {
|
||||||
|
"runOn": "folderOpen",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "🖥️ SSH ASUSTOR",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "ssh asustor",
|
||||||
|
"isBackground": true,
|
||||||
|
"problemMatcher": [],
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "always",
|
||||||
|
"panel": "dedicated",
|
||||||
|
"group": "ssh",
|
||||||
|
},
|
||||||
|
"runOptions": {
|
||||||
|
"runOn": "folderOpen",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,106 +0,0 @@
|
|||||||
# 08-Infrastructure
|
|
||||||
|
|
||||||
คู่มือการตั้งค่า Infrastructure สำหรับ **NAP-DMS LCBP3** (Laem Chabang Port Phase 3 - Document Management System)
|
|
||||||
|
|
||||||
> 📍 **Platform:** QNAP (Container Station) + ASUSTOR (Portainer)
|
|
||||||
> 🌐 **Domain:** `*.np-dms.work` (IP: 159.192.126.103)
|
|
||||||
> 🔒 **Network:** `lcbp3` (Docker External Network)
|
|
||||||
> 📄 **Version:** v2.0.0 (Refactored for Stability)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🏢 Hardware Infrastructure
|
|
||||||
|
|
||||||
### Server Role Separation
|
|
||||||
|
|
||||||
#### QNAP TS-473A
|
|
||||||
| (Application & Database Server)|||
|
|
||||||
| :--------------------- | :---------------- | :-------------------- |
|
|
||||||
| ✔ Application Runtime |✔ API / Web | ✔ Database (Primary) |
|
|
||||||
| ✔ High CPU / RAM usage | ✔ Worker / Queue | ✖ No long-term backup |
|
|
||||||
| Container Station (UI) | 32GB RAM (Capped) | AMD Ryzen V1500B |
|
|
||||||
|
|
||||||
#### ASUSTOR AS5403T
|
|
||||||
| (Infrastructure & Backup Server) |||
|
|
||||||
| :--------------------- | :---------------- | :------------------- |
|
|
||||||
| ✔ File Storage | ✔ Backup Target | ✔ Docker Infra |
|
|
||||||
|✔ Monitoring / Registry | ✔ Log Aggregation | ✖ No heavy App logic |
|
|
||||||
| Portainer (Manage All) | 16GB RAM | Intel Celeron @2GHz |
|
|
||||||
|
|
||||||
### Servers Specification & Resource Allocation
|
|
||||||
|
|
||||||
| Device | Model | CPU | RAM | Resource Policy | Role |
|
|
||||||
| :---------- | :------ | :---------------------- | :--- | :------------------ | :--------------------- |
|
|
||||||
| **QNAP** | TS-473A | AMD Ryzen V1500B | 32GB | **Strict Limits** | Application, DB, Cache |
|
|
||||||
| **ASUSTOR** | AS5403T | Intel Celeron @ 2.00GHz | 16GB | **Moderate Limits** | Infra, Backup, Monitor |
|
|
||||||
|
|
||||||
### Service Distribution by Server
|
|
||||||
|
|
||||||
#### QNAP TS-473A (Application Stack)
|
|
||||||
|
|
||||||
| Category | Service | Strategy | Resource Limit (Est.) |
|
|
||||||
| :-------------- | :------------------------ | :------------------------------ | :-------------------- |
|
|
||||||
| **Web App** | Next.js (Frontend) | Single Instance | 2.0 CPU / 2GB RAM |
|
|
||||||
| **Backend API** | NestJS | **2 Replicas** (Load Balanced) | 2.0 CPU / 1.5GB RAM |
|
|
||||||
| **Database** | MariaDB (Primary) | Performance Tuned (Buffer Pool) | 4.0 CPU / 5GB RAM |
|
|
||||||
| **Worker** | Redis + BullMQ Worker | **Standalone + AOF** | 2.0 CPU / 1.5GB RAM |
|
|
||||||
| **Search** | Elasticsearch | **Heap Locked (2GB)** | 2.0 CPU / 4GB RAM |
|
|
||||||
| **API Gateway** | NPM (Nginx Proxy Manager) | SSL Termination | 1.0 CPU / 512MB RAM |
|
|
||||||
| **Workflow** | n8n | Automation | 1.0 CPU / 1GB RAM |
|
|
||||||
| **Code** | Gitea | Git Repository | 1.0 CPU / 1GB RAM |
|
|
||||||
|
|
||||||
#### ASUSTOR AS5403T (Infrastructure Stack)
|
|
||||||
|
|
||||||
| Category | Service | Notes |
|
|
||||||
| :--------------- | :------------------ | :------------------------------ |
|
|
||||||
| **File Storage** | NFS / SMB | Shared volumes for backup |
|
|
||||||
| **Backup** | Restic / Borg | Pull-based Backup (More Safe) |
|
|
||||||
| **Docker Infra** | Registry, Portainer | Container image registry, mgmt |
|
|
||||||
| **Monitoring** | Uptime Kuma | Service availability monitoring |
|
|
||||||
| **Metrics** | Prometheus, Grafana | Cross-Server Scraping |
|
|
||||||
| **Log** | Loki / Syslog | Centralized logging |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔄 Data Flow Architecture
|
|
||||||
┌──────────────┐
|
|
||||||
│ User │
|
|
||||||
└──────┬───────┘
|
|
||||||
│ HTTPS (443)
|
|
||||||
▼
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ QNAP TS-473A │
|
|
||||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
||||||
│ │ Nginx Proxy Manager (NPM) │ │
|
|
||||||
│ │ SSL Termination + Round Robin LB │ │
|
|
||||||
│ └───────────────────────┬─────────────────────────────────┘ │
|
|
||||||
│ │ │
|
|
||||||
│ ┌───────────────────────▼─────────────────────────────────┐ │
|
|
||||||
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ | │
|
|
||||||
│ │ │ Next.js │─▶│ NestJS │ │ NestJS │ | │
|
|
||||||
│ │ │ (Frontend) │ │ (Replica 1) │ │ (Replica 2) │ │ │
|
|
||||||
│ │ └──────────────┘ └──────┬───────┘ └──────┬───────┘ │ │
|
|
||||||
│ │ │ │ │ │
|
|
||||||
│ │ ┌─────────────────────────┼────────────────┼────┐ │ │
|
|
||||||
│ │ ▼ ▼ ▼ ▼ │ │
|
|
||||||
│ │ ┌──────────┐ ┌──────────┐ ┌─────────────┐ │ │
|
|
||||||
│ │ │ MariaDB │ │ Redis │ │Elasticsearch│ │ │
|
|
||||||
│ │ │ (Primary)│ │(Persist.)│ │ (Search) │ │ │
|
|
||||||
│ │ └────┬─────┘ └──────────┘ └─────────────┘ │ │
|
|
||||||
│ └──────┼──────────────────────────────────────────────────┘ │
|
|
||||||
│ └──────┼────────────────────────────────────────────────────┘
|
|
||||||
| Local Dump -> Restic Pull (Cross-Server)
|
|
||||||
▼
|
|
||||||
┌──────────────────────────────────────────────────────────────┐
|
|
||||||
│ ASUSTOR AS5403T │
|
|
||||||
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
||||||
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
|
|
||||||
│ │ │ Backup │ │ Registry │ │ Uptime │ │ │
|
|
||||||
│ │ │ (Restic) │ │ (Docker) │ │ Kuma │ │ │
|
|
||||||
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
|
|
||||||
│ │ │Prometheus│───▶│ Grafana │ │ Loki │ │ │
|
|
||||||
│ │ │(Scraper) │ │(Dashboard)│ │ (Logs) │ │ │
|
|
||||||
│ │ └──────────┘ └──────────┘ └──────────┘ ││
|
|
||||||
│ └──────────────────────────────────────────────────────────┘│ └──────────────────────────────────────────────────────────────┘
|
|
||||||
219
specs/08-infrastructure/SSH_setting.md
Normal file
219
specs/08-infrastructure/SSH_setting.md
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
# SSH Setting — QNAP & ASUSTOR
|
||||||
|
|
||||||
|
> คู่มือการตั้งค่าและใช้งาน SSH สำหรับ NAS ทั้ง 2 เครื่องในโปรเจกต์ NAP-DMS
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 ข้อมูลการเชื่อมต่อ
|
||||||
|
|
||||||
|
| รายการ | QNAP (TS-464) | ASUSTOR (AS5402T) |
|
||||||
|
| ------------- | ------------------ | ------------------- |
|
||||||
|
| **Role** | Application Server | Monitoring / Backup |
|
||||||
|
| **IP** | `192.168.10.8` | `192.168.10.9` |
|
||||||
|
| **SSH Port** | `22` | `22` |
|
||||||
|
| **Username** | `nattanin` | `nattanin` |
|
||||||
|
| **SSH Alias** | `qnap` | `asustor` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. เปิดใช้งาน SSH บน NAS
|
||||||
|
|
||||||
|
### 1.1 QNAP
|
||||||
|
|
||||||
|
1. เข้า **QTS Web UI** → `http://192.168.10.8:8080`
|
||||||
|
2. ไปที่ **Control Panel → Network & File Services → Telnet / SSH**
|
||||||
|
3. เปิด ✅ **Allow SSH connection**
|
||||||
|
4. ตั้ง Port เป็น `22`
|
||||||
|
5. คลิก **Apply**
|
||||||
|
|
||||||
|
### 1.2 ASUSTOR
|
||||||
|
|
||||||
|
1. เข้า **ADM Web UI** → `http://192.168.10.9:8000`
|
||||||
|
2. ไปที่ **Settings → Terminal & SNMP**
|
||||||
|
3. เปิด ✅ **Enable SSH service**
|
||||||
|
4. ตั้ง Port เป็น `22`
|
||||||
|
5. คลิก **Apply**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. ตั้งค่า SSH Key (Client → NAS)
|
||||||
|
|
||||||
|
### 2.1 สร้าง SSH Key (ทำครั้งเดียวบนเครื่อง Client)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# ตรวจสอบว่ามี key อยู่แล้วหรือไม่
|
||||||
|
ls ~/.ssh/id_ed25519*
|
||||||
|
|
||||||
|
# ถ้ายังไม่มี → สร้างใหม่
|
||||||
|
ssh-keygen -t ed25519 -C "nattanin@np-dms"
|
||||||
|
```
|
||||||
|
|
||||||
|
> **หมายเหตุ:** กด Enter ผ่าน passphrase ได้ หรือตั้ง passphrase เพื่อความปลอดภัยเพิ่มเติม
|
||||||
|
|
||||||
|
### 2.2 คัดลอก Public Key ไปยัง NAS
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# QNAP
|
||||||
|
ssh-copy-id -i ~/.ssh/id_ed25519.pub nattanin@192.168.10.8
|
||||||
|
|
||||||
|
# ASUSTOR
|
||||||
|
ssh-copy-id -i ~/.ssh/id_ed25519.pub nattanin@192.168.10.9
|
||||||
|
```
|
||||||
|
|
||||||
|
> **ถ้า `ssh-copy-id` ไม่มีบน Windows** ให้ทำ manual:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# อ่าน public key
|
||||||
|
cat ~/.ssh/id_ed25519.pub
|
||||||
|
|
||||||
|
# SSH เข้า NAS ด้วย password ก่อน แล้วเพิ่ม key
|
||||||
|
ssh nattanin@192.168.10.8
|
||||||
|
mkdir -p ~/.ssh && chmod 700 ~/.ssh
|
||||||
|
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... your-email@example.com" >> ~/.ssh/authorized_keys
|
||||||
|
chmod 600 ~/.ssh/authorized_keys
|
||||||
|
exit
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 ทดสอบการเชื่อมต่อ
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# ต้องเข้าได้โดยไม่ต้องใส่ password
|
||||||
|
ssh nattanin@192.168.10.8
|
||||||
|
ssh nattanin@192.168.10.9
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. ตั้งค่า SSH Config (Client)
|
||||||
|
|
||||||
|
ไฟล์: `~/.ssh/config` (Windows: `C:\Users\<username>\.ssh\config`)
|
||||||
|
|
||||||
|
```ssh-config
|
||||||
|
Host gitea
|
||||||
|
HostName git.np-dms.work
|
||||||
|
User git
|
||||||
|
Port 2222
|
||||||
|
IdentityFile ~/.ssh/id_ed25519
|
||||||
|
IdentitiesOnly yes
|
||||||
|
|
||||||
|
Host qnap
|
||||||
|
HostName 192.168.10.8
|
||||||
|
User nattanin
|
||||||
|
Port 22
|
||||||
|
IdentityFile ~/.ssh/id_ed25519
|
||||||
|
IdentitiesOnly yes
|
||||||
|
|
||||||
|
Host asustor
|
||||||
|
HostName 192.168.10.9
|
||||||
|
User nattanin
|
||||||
|
Port 22
|
||||||
|
IdentityFile ~/.ssh/id_ed25519
|
||||||
|
IdentitiesOnly yes
|
||||||
|
```
|
||||||
|
|
||||||
|
### การใช้งาน Alias
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# แทนที่จะพิมพ์ ssh nattanin@192.168.10.8
|
||||||
|
ssh qnap
|
||||||
|
|
||||||
|
# แทนที่จะพิมพ์ ssh nattanin@192.168.10.9
|
||||||
|
ssh asustor
|
||||||
|
|
||||||
|
# Git push ไป Gitea (ใช้ alias gitea)
|
||||||
|
git push gitea main
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. คำสั่ง SSH ที่ใช้บ่อย
|
||||||
|
|
||||||
|
### 4.1 การเชื่อมต่อ
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# เชื่อมต่อปกติ
|
||||||
|
ssh qnap
|
||||||
|
ssh asustor
|
||||||
|
|
||||||
|
# เชื่อมต่อพร้อมระบุ port (กรณี port ไม่ใช่ 22)
|
||||||
|
ssh -p 2222 nattanin@192.168.10.8
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 คัดลอกไฟล์ (SCP)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# คัดลอกไฟล์จาก Local → NAS
|
||||||
|
scp ./myfile.txt qnap:/share/np-dms/data/
|
||||||
|
|
||||||
|
# คัดลอกไฟล์จาก NAS → Local
|
||||||
|
scp qnap:/share/np-dms/data/myfile.txt ./
|
||||||
|
|
||||||
|
# คัดลอก Folder (recursive)
|
||||||
|
scp -r ./myfolder qnap:/share/np-dms/data/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 รันคำสั่งบน NAS โดยไม่ต้อง Login
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# ดู Docker containers ที่กำลังรัน
|
||||||
|
ssh qnap "docker ps"
|
||||||
|
|
||||||
|
# ดู Disk usage
|
||||||
|
ssh qnap "df -h"
|
||||||
|
|
||||||
|
# ดู logs ของ container
|
||||||
|
ssh qnap "docker logs --tail 50 lcbp3-backend"
|
||||||
|
|
||||||
|
# Restart container
|
||||||
|
ssh qnap "docker restart lcbp3-backend"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.4 Port Forwarding (Tunnel)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Forward port 3306 ของ QNAP มาที่ localhost:3306 (MariaDB)
|
||||||
|
ssh -L 3306:localhost:3306 qnap
|
||||||
|
|
||||||
|
# Forward port 9200 (Elasticsearch)
|
||||||
|
ssh -L 9200:localhost:9200 qnap
|
||||||
|
|
||||||
|
# Forward port 3000 (Grafana จาก ASUSTOR)
|
||||||
|
ssh -L 3000:localhost:3000 asustor
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Hardening (เพิ่มความปลอดภัย)
|
||||||
|
|
||||||
|
> กำหนดค่าบน NAS แต่ละเครื่อง — ไฟล์: `/etc/ssh/sshd_config`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# ปิด login ด้วย password (ใช้ key เท่านั้น)
|
||||||
|
PasswordAuthentication no
|
||||||
|
|
||||||
|
# ปิด root login
|
||||||
|
PermitRootLogin no
|
||||||
|
|
||||||
|
# อนุญาตเฉพาะ user ที่ต้องการ
|
||||||
|
AllowUsers nattanin
|
||||||
|
|
||||||
|
# Restart SSH service (QNAP)
|
||||||
|
/etc/init.d/login_server.sh restart
|
||||||
|
|
||||||
|
# Restart SSH service (ASUSTOR)
|
||||||
|
/etc/init.d/sshd restart
|
||||||
|
```
|
||||||
|
|
||||||
|
> ⚠️ **คำเตือน:** ก่อนปิด `PasswordAuthentication` ให้แน่ใจว่า SSH Key ใช้งานได้แล้ว มิฉะนั้นจะเข้าไม่ได้ — ต้อง login ผ่าน Web UI เพื่อแก้ไข
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Troubleshooting
|
||||||
|
|
||||||
|
| ปัญหา | สาเหตุ | วิธีแก้ |
|
||||||
|
| ------------------------------- | -------------------------- | --------------------------------------------------------------------- |
|
||||||
|
| `Connection refused` | SSH ไม่ได้เปิดบน NAS | เปิด SSH ผ่าน Web UI (ตาม Section 1) |
|
||||||
|
| `Permission denied (publickey)` | Key ไม่ตรงหรือ permission ผิด | ตรวจ `chmod 700 ~/.ssh` และ `chmod 600 ~/.ssh/authorized_keys` บน NAS |
|
||||||
|
| `Host key verification failed` | IP เปลี่ยนแต่ key เก่ายังอยู่ | `ssh-keygen -R 192.168.10.8` แล้วเชื่อมต่อใหม่ |
|
||||||
|
| `Connection timed out` | Firewall block หรือ IP ผิด | ตรวจ ACL ใน `03_Securities.md` และ ping ทดสอบ |
|
||||||
|
| `Network is unreachable` | อยู่คนละ VLAN / subnet | ตรวจ routing ใน `02_Network_daigram.md` |
|
||||||
Reference in New Issue
Block a user