영역 통합 연결: 공통 화면 틀 우선 사용, AI Wallet 계정명 규칙, 상품정책 조회 이름, 주기 정리 작업 등록
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
b1c06a0304
commit
403d0fb845
@@ -29,6 +29,9 @@ export async function buildApp(deps, opts = {}) {
|
||||
const mod = await import(file); if (typeof mod.register !== 'function') continue;
|
||||
await mod.register(app, deps); loaded.push(a);
|
||||
}
|
||||
// [통합] 영역 사이의 느슨한 연결: 상품정책 조회 이름 맞춤, 주기 정리 작업 등록
|
||||
if (!deps.productPolicy && deps.commerce?.appliedProduct) deps.productPolicy = { activeFor: async (groupId) => deps.commerce.appliedProduct(groupId) };
|
||||
if (deps.scheduler?.every) for (const [name, fn] of [['sites.sweep', () => deps.sites?.sweep?.()], ['domains.sweep', () => deps.domains?.sweep?.()], ['source.sweep', () => deps.source?.sweep?.()]]) deps.scheduler.every(name, 10 * 60 * 1000, fn);
|
||||
deps.log(`[portal] 영역 등록: ${loaded.join(', ')}`);
|
||||
return app;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ export async function register(app, deps) {
|
||||
app.get('/api/commerce/orders', (ctx) => { requireGroup(ctx); return { orders: ord.listOrders(store, { groupId: ctx.group.id }).map((o) => orderView(store, o)) }; });
|
||||
app.get('/api/commerce/orders/:id', (ctx) => { requireGroup(ctx); const o = ord.getOrder(store, ctx.params.id, ctx.group.id); if (!o) throw new HttpError(404, 'not_found', '주문이 없습니다'); return { order: orderView(store, o) }; });
|
||||
app.post('/api/commerce/orders/:id/cancel', (ctx) => { requireBilling(ctx); return { ok: true, order: orderView(store, ord.cancelOrder(store, ctx.params.id, ctx.group.id, ctx.user.id)) }; });
|
||||
app.get('/api/commerce/billing', async (ctx) => { requireGroup(ctx); const s = ord.billingSummary(store, ctx.group.id); let wallet = null; try { wallet = deps.webmaker?.credits ? await deps.webmaker.credits(ctx.user.id, ctx.group.id) : null; } catch (e) { wallet = { error: 'unavailable', message: 'AI Wallet 정보를 지금 확인할 수 없습니다(정보 확인 지연)' }; }
|
||||
app.get('/api/commerce/billing', async (ctx) => { requireGroup(ctx); const s = ord.billingSummary(store, ctx.group.id); let wallet = null; try { wallet = deps.webmaker?.credits ? await deps.webmaker.credits(deps.webmaker.loginFor ? deps.webmaker.loginFor(ctx.user, ctx.group) : ctx.user.id) /* [통합] Webmaker 계정명은 어댑터의 loginFor 규칙 */ : null; } catch (e) { wallet = { error: 'unavailable', message: 'AI Wallet 정보를 지금 확인할 수 없습니다(정보 확인 지연)' }; }
|
||||
return { ...s, orders: ord.listOrders(store, { groupId: ctx.group.id }).map((o) => orderView(store, o)), hold: ord.openHold(store, ctx.group.id), overage: ord.listOverage(store, ctx.group.id), wallet: wallet || { available: false, message: 'AI Wallet 은 Webmaker 제작 화면에서 확인·충전합니다(호스팅 사용량과 별도)' }, domainRegistration: ord.domainRegistration(store), entitlements: quota.summary(store, ctx.group.id) }; });
|
||||
app.post('/api/commerce/downsize/preview', async (ctx) => { requireBilling(ctx); const b = await ctx.body(); const pv = cat.getProductVersion(store, b.productVersionId); if (!pv) throw new HttpError(404, 'product_not_found', '상품이 없습니다'); const priced = ord.priceItems(pv, { includeBase: true, items: b.items || [] }); return { ...ord.evaluateDownsize(store, ctx.group.id, pv, priced.items), quote: priced }; });
|
||||
app.post('/api/commerce/downsize/request', async (ctx) => { requireBilling(ctx); const b = await ctx.body(); const r = ord.requestDownsize(store, { groupId: ctx.group.id, userId: ctx.user.id, userVerified: !!ctx.user.email_verified_at, productVersionId: b.productVersionId, items: b.items || [], acceptTerms: !!b.acceptTerms }); return { ok: true, fits: r.fits, shortages: r.shortages, order: r.order ? orderView(store, r.order) : null, hold: r.hold || null }; });
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/** portal/domains/ui.mjs — 도메인 화면 뼈대(목록·연결·상세). 데이터는 /ui/domains/domains.js 가 채운다. */
|
||||
import { esc } from '../../lib/http.mjs';
|
||||
import { t } from '../../lib/i18n.mjs';
|
||||
const shell = (ctx, deps, o) => deps.page({ ...o, user: ctx.user, group: ctx.group, groups: ctx.groups || [], unread: ctx.unread || 0, csrf: ctx.session ? ctx.session.csrf : '', scripts: ['/ui/domains/domains.js'] });
|
||||
const shell = (ctx, deps, o) => deps.shell ? deps.shell(ctx, o) /* [통합] 인증 영역 공통 틀 우선 */ : deps.page({ ...o, user: ctx.user, group: ctx.group, groups: ctx.groups || [], unread: ctx.unread || 0, csrf: ctx.session ? ctx.session.csrf : '', scripts: ['/ui/domains/domains.js'] });
|
||||
export const listPage = (ctx, deps) => shell(ctx, deps, { title: t('domains.title'), nav: 'domains', crumbs: [{ label: t('nav.dashboard'), href: '/' }, { label: t('domains.title') }], actions: [{ label: t('domains.connect'), href: '/domains/connect', kind: 'primary' }, { label: '도메인 구매·DNS 자동화(Webmaker)', href: '/sso/webmaker?path=/' }], body: `<div id="list" data-page="domains-list">${deps.statePanel('loading')}</div><p class="hint">도메인 등록·갱신·네임서버·레코드 자동화는 Webmaker 의 도메인 탭에서 기존 기능 그대로 제공됩니다. 도메인 등록 기능이 제공되지 않는 환경에서는 외부 등록기관에서 구매한 도메인을 여기서 연결하세요.</p>` });
|
||||
export const connectPage = (ctx, deps, { siteId = '' } = {}) => shell(ctx, deps, { title: t('domains.connect'), nav: 'domains', crumbs: [{ label: t('domains.title'), href: '/domains' }, { label: t('domains.connect') }], body: `<form id="connect" class="card" data-page="domains-connect" data-site="${esc(siteId)}"><ol class="hint"><li>도메인 입력</li><li>웹·메일 용도 선택</li><li>소유 확인(TXT)</li><li>필요한 DNS 값 안내</li><li>DNS 반영 확인</li><li>HTTPS 인증서 발급</li><li>외부 접속 확인</li><li>대표 도메인 지정</li></ol>
|
||||
<div class="f"><label for="name">도메인</label><input id="name" name="name" placeholder="example.com" required><span class="hint">외부 등록기관에서 구매한 도메인도 연결할 수 있습니다. www 없이 입력하세요</span><span class="err" data-err="name"></span></div>
|
||||
|
||||
+2
-1
@@ -59,4 +59,5 @@ export function externalPage(ctx, deps, site) {
|
||||
return shell(ctx, deps, { title: t('sites.external.title'), nav: 'sites', crumbs: [{ label: t('sites.title'), href: '/sites' }, { label: site.name, href: `/sites/${site.id}` }, { label: t('sites.external.title') }], body: `<div data-page="site-external" data-site="${esc(site.id)}"><div id="external">${deps.statePanel('loading')}</div></div>` });
|
||||
}
|
||||
export function deployPage(ctx, deps, site) { return detailPage(ctx, deps, site, { tab: 'deploy' }); }
|
||||
function shell(ctx, deps, o) { return deps.page({ ...o, user: ctx.user, group: ctx.group, groups: ctx.groups || [], unread: ctx.unread || 0, csrf: ctx.session ? ctx.session.csrf : '', scripts: ['/ui/sites/sites.js'] }); }
|
||||
function shell(ctx, deps, o) { if (deps.shell) return deps.shell(ctx, o); // [통합] 인증 영역의 공통 틀(그룹 목록·읽지 않은 알림·CSRF 포함)을 우선 쓴다
|
||||
return deps.page({ ...o, user: ctx.user, group: ctx.group, groups: ctx.groups || [], unread: ctx.unread || 0, csrf: ctx.session ? ctx.session.csrf : '', scripts: ['/ui/sites/sites.js'] }); }
|
||||
|
||||
Reference in New Issue
Block a user