Files
AriWeb-Suite/hal/modules/index.mjs
T

86 lines
14 KiB
JavaScript
Raw Normal View History

/**
* hal/modules/index.mjs — 모듈 기술서(descriptor) 모음(§32) + [0.3.17] 역할별 서버 프로필·모듈 안내.
*
* HAL 이 어떤 모듈을 어떤 서버에 어떻게 설치·갱신·건강 확인하는지를 데이터로 둔다. Agent 는 이 기술서를 작업 payload 로 받아 그대로 수행하므로
* Agent 바이너리는 모듈별 지식을 갖지 않는다(새 모듈을 더할 때 HAL 만 바꾸면 된다).
*
* 기술서 모양
* name 모듈 이름(릴리스 artifact 이름과 같다: <name>-<version>.tar.gz)
* label 화면 표시 이름
* roles 설치할 수 있는 서버 역할
* layout 'releases'(= /srv/ariweb/<name>/releases/<ver> + current) — 도입 모듈도 같은 틀을 쓰되 데이터 경로는 기존 것을 유지한다 / 'agent-feature'(Agent 안의 기능, artifact 없음)
* unit systemd 유닛 이름(도입 모듈은 서버에 이미 있는 이름을 쓸 수 있어 server.facts.units 로 덮어쓴다)
* unitTemplate deploy/units/<file>.tmpl — 없으면 기존 유닛을 그대로 두고 WorkingDirectory 만 current 심링크가 가리키게 한다
* health { url, expect } 내부 건강 확인(127.0.0.1)
* version 실제 실행 버전을 읽는 방법: { file:'package.json' } (current/package.json 의 version) 또는 { http:'/api/health', field:'version' }
* preStop/postStart Agent 가 실행할 추가 절차 이름(agent/jobs/module.mjs 가 아는 것만)
* dataDirs 절대 건드리지 않는 데이터 경로(검증·백업 대상 안내용)
*
* [0.3.17] 이름 변경: observability → hawkeye(HawkEye, 전체 시스템 상황 점검). 옛 이름은 descriptor() 별칭으로 받고 DB 행은 hal_0008_hawkeye 마이그레이션이 바꾼다.
* [0.3.17] HAL 은 배포 대상이 아니다: HAL 은 Suite 전체를 설치하기 전에 먼저 설치하는 도구이며 HAL 서버에서 HAL 을 다시 배포하지 않는다(이중화·분산 미구성). DEPLOY_ORDER·DEPLOYABLE_MODULES 에서 뺀다.
*/
export const MODULES = {
hal: { name: 'hal', label: 'HAL', roles: ['control'], layout: 'releases', unit: 'ariweb-hal', unitTemplate: 'ariweb-hal.service.tmpl', user: 'master', port: 8900,
health: { url: 'http://127.0.0.1:8900/api/health', expect: 'ok' }, version: { http: 'http://127.0.0.1:8900/api/health', field: 'version' }, dataDirs: ['/srv/ariweb/hal/data'], selfUpdate: true, deployable: false },
portal: { name: 'portal', label: '고객 포털', roles: ['control', 'web'], layout: 'releases', unit: 'ariweb-portal', unitTemplate: 'ariweb-portal.service.tmpl', user: 'master', port: 8910,
health: { url: 'http://127.0.0.1:8910/api/health', expect: 'ok' }, version: { http: 'http://127.0.0.1:8910/api/health', field: 'version' }, dataDirs: ['/srv/ariweb/portal/data'] },
agent: { name: 'agent', label: 'Node Agent', roles: ['control', 'web', 'mail', 'backup'], layout: 'releases', unit: 'ariweb-agent', unitTemplate: 'ariweb-agent.service.tmpl', user: 'master',
health: { heartbeat: true }, version: { file: 'package.json' }, dataDirs: ['/srv/ariweb/agent/data'], selfUpdate: true },
// 도입 모듈: Webmaker(WebHosting_AI_agent 1.8.0 — 0.2.0 에서 직접 편집(시각 편집기) 포함; 0.1.1 은 1.7.3). 기존 서버 배치(/srv/webmaker/current, /data/webmaker, 유닛 webmaker, 포트 3080)를 존중한다 — install 은 releases 에 풀고 current 를 바꾸며,
// 유닛은 서버에 이미 있고 <appDir>/current 를 가리키면 그대로 둔다(keepExistingUnit — 운영 서버의 강화 지시문·ReadWritePaths 보존, 실제 환경 결함 수정). 없을 때만 deploy/units/webmaker.service.tmpl 로 만든다.
// 원본 deploy/remote-update.sh 의 절차(백업→전송→교체→재시작→확인)를 Agent 가 같은 순서로 수행한다. 실제 버전은 REVISION.json 의 Suite 릴리스 버전(package.json 1.8.0 은 upstreamVersion).
webmaker: { name: 'webmaker', label: 'Webmaker', roles: ['web'], layout: 'releases', unit: 'webmaker', unitTemplate: 'webmaker.service.tmpl', keepExistingUnit: true, user: 'master', port: 3080, baseDir: '/srv/webmaker',
health: { url: 'http://127.0.0.1:3080/api/auth/csrf', expect: 'any' }, version: { file: 'package.json' }, preStop: ['webmaker.backupData'], postStart: ['webmaker.unitCheck'], dataDirs: ['/data/webmaker', '/var/www/webmaker-sites'], extraUnits: ['webmaker-setup'] },
// 도입 모듈: AriMail 1.0.11. 서버마다 유닛 이름이 다르다(arihakdang-mail / arimail / heart-mail) — 서버 facts.arimail.unit 이 정한다. 설치는 원본 deploy/deploy.sh 와 같은 절차(테스트→릴리스 복사→DB 백업·마이그레이션→재시작→health)를 Agent 가 수행한다.
// 실행 계정이 Agent 와 다르면(ariweb.net 호스트의 arihakdang) Agent 가 deploy/module-as-user.sh 로 그 계정이 파일 작업을 하게 한다(실제 환경 결함 수정). 마이그레이션은 유닛의 EnvironmentFile(DB_FILE)을 같은 계정으로 읽어 실행한다.
arimail: { name: 'arimail', label: 'AriMail', roles: ['mail'], layout: 'releases', unit: 'arimail', unitTemplate: null, user: null, port: 8790,
health: { url: 'http://127.0.0.1:8790/api/health', expect: 'ok' }, version: { file: 'package.json' }, preStop: ['arimail.backupDb'], postStart: ['arimail.migrate', 'arimail.haraka'], dataDirs: ['<appDir>/data'], copyPaths: ['server', 'web', 'lang', 'migrations', 'package.json', 'haraka'] },
backup: { name: 'backup', label: '백업', roles: ['backup', 'web'], layout: 'agent-feature', unit: null, health: { feature: 'backup' }, version: { agent: true }, dataDirs: ['/srv/ariweb/backups'] },
hawkeye: { name: 'hawkeye', label: 'HawkEye', roles: ['control', 'web', 'mail', 'backup'], layout: 'agent-feature', unit: null, health: { feature: 'metrics' }, version: { agent: true }, dataDirs: [], legacyNames: ['observability'] },
};
/** 옛 이름 → 새 이름(요청·설정·DB 에 남은 observability 를 받아 준다) */
export const LEGACY_MODULE_NAMES = { observability: 'hawkeye' };
export const canonicalModuleName = (name) => LEGACY_MODULE_NAMES[name] || name;
/** 릴리스 artifact 로 배포되는 모듈(agent-feature 는 Agent 안의 기능이라 별도 artifact 가 없다). HAL 도 artifact 는 있지만 배포 대상은 아니다. */
export const ARTIFACT_MODULES = Object.values(MODULES).filter((m) => m.layout === 'releases').map((m) => m.name);
/** [0.3.17] 배포 순서(HAL 제외): agent 가 가장 먼저(다른 모듈 설치를 수행하는 주체) → portal → 도입 모듈 → 백업 → HawkEye */
export const DEPLOY_ORDER = ['agent', 'portal', 'webmaker', 'arimail', 'backup', 'hawkeye'];
/** [0.3.17] 릴리스 화면·배포 API 가 다루는 모듈(HAL 제외) */
export const DEPLOYABLE_MODULES = DEPLOY_ORDER.slice();
export function descriptor(name) { const d = MODULES[canonicalModuleName(name)]; if (!d) throw Object.assign(new Error(`알 수 없는 모듈: ${name}`), { code: 'unknown_module', status: 404 }); return d; }
/**
* [0.3.17] 모듈 안내(릴리스 화면·AI 스킬): 무엇을 하는지, 어떤 역할 서버에, 무엇이 먼저 있어야 하는지(requires), 무엇을 제공하는지(provides), 설치 방법.
*/
export const MODULE_GUIDES = {
agent: { label: 'Node Agent', description: '각 관리 서버에서 HAL 의 작업(설치·갱신·건강 확인·백업·지표)을 수행하는 상주 프로세스. 다른 모든 모듈은 Agent 를 통해 배포된다.', roles: MODULES.agent.roles, requires: ['hal'], provides: ['module.install', 'heartbeat', 'metrics', 'backup'], install: 'HAL 서버의 Agent 는 부트스트랩(bootstrap-hal.sh)이 함께 설치·승인한다(0.4.1). 다른 서버는 서버 등록 → 일회용 등록 정보(HAL 인증서가 사설 CA 면 핀이 붙은 명령)로 Agent 설치 → 승인. 갱신은 이 화면의 배포(자기 갱신: 설치 뒤 자동 재시작).', singleton: false },
portal: { label: '고객 포털', description: '고객 가입·상품·사이트·메일·도메인·백업·결제를 하나로 잇는 사용자 접근 화면과 API. HAL 과 서비스 키로 연결되어 작업을 HAL 에 맡긴다.', roles: MODULES.portal.roles, requires: ['agent', 'hal 연동 키(link-portal)'], provides: ['고객 화면 my.ariweb.net', '/internal/ops 운영 중계'], install: '포털 서버 프로필로 통합 배포하거나 이 화면에서 portal 배포. 포털은 **하나만** 운영한다 — 이미 있으면 설치 대신 "포털 이전"을 쓴다(새 서버 설치 → 데이터·DNS 전환 확인 → 옛 서버 비활성).', singleton: true },
webmaker: { label: 'Webmaker', description: 'AI 사이트 제작 도구(도입 모듈). 제작 화면·직접 편집·배포 요청을 제공하고, 사용자 사이트는 웹호스팅 서버로 배포된다.', roles: MODULES.webmaker.roles, requires: ['agent', 'portal(SSO·배포 연동)', 'webhosting 서버 1대 이상(배포 대상)'], provides: ['사이트 제작·편집', '배포 요청'], install: 'Webmaker 서버 프로필(이 서버에서는 Webmaker 만 동작)로 통합 배포하거나 web 역할 서버에 배포.', singleton: false },
arimail: { label: 'AriMail', description: '메일 서비스(도입 모듈): 웹메일 접속, POP·IMAP·SMTP, 도메인별 메일함. HAL 관리형 메일 서비스 등록부와 연결된다.', roles: MODULES.arimail.roles, requires: ['agent', 'mail 역할 서버', 'DNS(MX·SPF·DKIM)'], provides: ['webmail', 'POP/IMAP/SMTP'], install: 'AriMail 서버 프로필로 통합 배포하거나 mail 역할 서버에 배포. 기존 AriMail 인스턴스는 mail-import 로 가져온다.', singleton: false },
backup: { label: '백업', description: 'Agent 안의 백업 기능: 사이트·메일 백업 생성·저장·검증·복원. 별도 artifact 없이 Agent 와 함께 설치된다.', roles: MODULES.backup.roles, requires: ['agent'], provides: ['backup.create/verify/restore'], install: '백업 서버 프로필로 통합 배포(backup 역할). 운영 서버와 분리된 저장 서버를 권장.', singleton: false },
hawkeye: { label: 'HawkEye', description: '전체 시스템 상황 점검(옛 이름 observability): 서버 지표·시계 차이·디스크 고갈·모듈 건강을 heartbeat 로 모아 운영 현황과 사건으로 보인다. Agent 안의 기능.', roles: MODULES.hawkeye.roles, requires: ['agent'], provides: ['지표 수집', '건강·사건 감지'], install: '모든 프로필에 포함되어 Agent 와 함께 설치된다. 별도 artifact 없음.', singleton: false },
};
/** [0.3.17] 모듈 사이의 연결(릴리스 화면 "상호 연결" 설명) */
export const MODULE_LINKS = [
'HAL ↔ Agent: 모든 서버 작업(설치·건강·백업·지표)은 Agent 가 HAL 에서 받아 수행한다. Agent 없이는 어떤 모듈도 배포되지 않는다.',
'HAL ↔ 포털: 서비스 키(link-portal)로 연결. 포털의 사이트 배포·백업·이전 요청은 HAL 작업으로 실행된다. 포털이 없으면 고객 화면이 없다.',
'포털 ↔ Webmaker: SSO·제작 화면 열기·배포 요청. Webmaker 가 만든 사이트는 웹호스팅 서버(web 역할, Agent 설치)로 배포된다.',
'포털 ↔ AriMail / HAL 메일 서비스 등록부: 메일 서비스·메일함 생성은 포털이 요청하고 AriMail(mail 역할)이 제공한다.',
'백업(Agent 기능) ↔ 포털: 백업 옵션·복원 요청은 포털이, 생성·저장·검증은 Agent 백업 기능이 한다.',
'HawkEye(Agent 기능) → HAL: 서버 지표·모듈 건강을 heartbeat 로 올려 운영 현황·사건·공개 상태 페이지에 반영한다.',
];
/**
* [0.3.17] 역할별 서버 프로필(통합 배포). 새 서버를 추가할 때 역할을 고르면 필요한 모듈을 한 번에 순서대로 배포하고 서버의 역할·슬롯을 기록한다.
* slots: 그 서버가 맡을 수 있는 수(배치 때 HAL 이 본다). 포털은 singleton(하나만).
*/
export const SERVER_PROFILES = {
webmaker: { id: 'webmaker', label: 'Webmaker 서버', role: 'web', modules: ['agent', 'webmaker', 'hawkeye'], description: '이 서버에서는 Webmaker 만 동작합니다(제작 화면·직접 편집).', slots: { webmaker: 1 } },
webhosting: { id: 'webhosting', label: '웹호스팅 서버', role: 'web', modules: ['agent', 'hawkeye'], description: '사용자의 웹 서비스를 제공합니다. Webmaker 의 배포 대상 서버가 됩니다(사이트 슬롯).', slots: { sites: 50 } },
arimail: { id: 'arimail', label: 'AriMail 서버', role: 'mail', modules: ['agent', 'arimail', 'hawkeye'], description: '사용자에게 메일 서비스(웹메일·POP·IMAP·SMTP)를 제공합니다.', slots: { mailServices: 20 } },
backup: { id: 'backup', label: '백업 서버', role: 'backup', modules: ['agent', 'backup', 'hawkeye'], description: '사용자 서비스의 백업을 저장·검증합니다. 운영 서버와 분리를 권장합니다.', slots: { backups: 500 } },
portal: { id: 'portal', label: '포털 서버', role: 'control', modules: ['agent', 'portal', 'hawkeye'], description: '사용자 접근을 위한 포털. 하나만 운영하며, 이미 있으면 설치 대신 이전(relocate)합니다.', slots: {}, singleton: true },
// [0.4.1] HAL 서버 자신: 부트스트랩(bootstrap-hal.sh)이 HAL 과 함께 Node Agent·HawkEye 를 설치·등록·승인해 부트스트랩을 닫는다. Web·Mail·Backup·Portal 은 포함하지 않는다(요구 2).
hal: { id: 'hal', label: 'HAL 서버', role: 'control', modules: ['agent', 'hawkeye'], description: 'HAL(제어 평면)이 있는 서버. 부트스트랩 때 Node Agent 와 HawkEye 가 함께 설치되어 HAL 서버 자체의 지표·건강이 운영 현황에 보입니다. 기능 모듈(포털·Webmaker·AriMail·백업)은 역할별 서버에 따로 배포합니다.', slots: {} },
};
export const profileOf = (id) => SERVER_PROFILES[id] || null;