@devoplus/one-sdk 0.0.0-stage → 0.2.0-next.117
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +268 -0
- package/README.md +893 -2
- package/bin/accessRequest.d.mts +96 -0
- package/bin/accessRequest.mjs +340 -0
- package/bin/appOps.d.mts +97 -0
- package/bin/appOps.mjs +336 -0
- package/bin/dataCommand.mjs +131 -0
- package/bin/devConfig.mjs +132 -0
- package/bin/one.mjs +341 -0
- package/bin/session.mjs +239 -0
- package/dist/agentInput.d.ts +9 -0
- package/dist/agentInput.d.ts.map +1 -0
- package/dist/agentInput.js +102 -0
- package/dist/agentInput.js.map +1 -0
- package/dist/agentTools.d.ts +83 -0
- package/dist/agentTools.d.ts.map +1 -0
- package/dist/agentTools.js +164 -0
- package/dist/agentTools.js.map +1 -0
- package/dist/ai.d.ts +67 -0
- package/dist/ai.d.ts.map +1 -0
- package/dist/ai.js +129 -0
- package/dist/ai.js.map +1 -0
- package/dist/audit.d.ts +21 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +58 -0
- package/dist/audit.js.map +1 -0
- package/dist/backend.d.ts +27 -0
- package/dist/backend.d.ts.map +1 -0
- package/dist/backend.js +63 -0
- package/dist/backend.js.map +1 -0
- package/dist/cache.d.ts +20 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +84 -0
- package/dist/cache.js.map +1 -0
- package/dist/client.d.ts +147 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +469 -0
- package/dist/client.js.map +1 -0
- package/dist/components.d.ts +235 -0
- package/dist/components.d.ts.map +1 -0
- package/dist/components.js +1491 -0
- package/dist/components.js.map +1 -0
- package/dist/context.d.ts +110 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +126 -0
- package/dist/context.js.map +1 -0
- package/dist/data/crud.d.ts +74 -0
- package/dist/data/crud.d.ts.map +1 -0
- package/dist/data/crud.js +614 -0
- package/dist/data/crud.js.map +1 -0
- package/dist/data/ddl.d.ts +76 -0
- package/dist/data/ddl.d.ts.map +1 -0
- package/dist/data/ddl.js +362 -0
- package/dist/data/ddl.js.map +1 -0
- package/dist/data/fold.d.ts +14 -0
- package/dist/data/fold.d.ts.map +1 -0
- package/dist/data/fold.js +76 -0
- package/dist/data/fold.js.map +1 -0
- package/dist/data/model.d.ts +113 -0
- package/dist/data/model.d.ts.map +1 -0
- package/dist/data/model.js +408 -0
- package/dist/data/model.js.map +1 -0
- package/dist/data/values.d.ts +29 -0
- package/dist/data/values.d.ts.map +1 -0
- package/dist/data/values.js +152 -0
- package/dist/data/values.js.map +1 -0
- package/dist/dexa.d.ts +44 -0
- package/dist/dexa.d.ts.map +1 -0
- package/dist/dexa.js +74 -0
- package/dist/dexa.js.map +1 -0
- package/dist/dexaCommand.d.ts +116 -0
- package/dist/dexaCommand.d.ts.map +1 -0
- package/dist/dexaCommand.js +226 -0
- package/dist/dexaCommand.js.map +1 -0
- package/dist/export/columns.d.ts +62 -0
- package/dist/export/columns.d.ts.map +1 -0
- package/dist/export/columns.js +127 -0
- package/dist/export/columns.js.map +1 -0
- package/dist/export/csv.d.ts +10 -0
- package/dist/export/csv.d.ts.map +1 -0
- package/dist/export/csv.js +30 -0
- package/dist/export/csv.js.map +1 -0
- package/dist/export/report.d.ts +19 -0
- package/dist/export/report.d.ts.map +1 -0
- package/dist/export/report.js +91 -0
- package/dist/export/report.js.map +1 -0
- package/dist/export/response.d.ts +14 -0
- package/dist/export/response.d.ts.map +1 -0
- package/dist/export/response.js +70 -0
- package/dist/export/response.js.map +1 -0
- package/dist/export/xlsx.d.ts +15 -0
- package/dist/export/xlsx.d.ts.map +1 -0
- package/dist/export/xlsx.js +167 -0
- package/dist/export/xlsx.js.map +1 -0
- package/dist/export/zip.d.ts +7 -0
- package/dist/export/zip.d.ts.map +1 -0
- package/dist/export/zip.js +83 -0
- package/dist/export/zip.js.map +1 -0
- package/dist/files.d.ts +52 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +137 -0
- package/dist/files.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/jobs.d.ts +57 -0
- package/dist/jobs.d.ts.map +1 -0
- package/dist/jobs.js +157 -0
- package/dist/jobs.js.map +1 -0
- package/dist/log.d.ts +22 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +107 -0
- package/dist/log.js.map +1 -0
- package/dist/notify.d.ts +41 -0
- package/dist/notify.d.ts.map +1 -0
- package/dist/notify.js +11 -0
- package/dist/notify.js.map +1 -0
- package/dist/org.d.ts +184 -0
- package/dist/org.d.ts.map +1 -0
- package/dist/org.js +114 -0
- package/dist/org.js.map +1 -0
- package/dist/people.d.ts +39 -0
- package/dist/people.d.ts.map +1 -0
- package/dist/people.js +88 -0
- package/dist/people.js.map +1 -0
- package/dist/problem.d.ts +12 -0
- package/dist/problem.d.ts.map +1 -0
- package/dist/problem.js +33 -0
- package/dist/problem.js.map +1 -0
- package/dist/runtime.d.ts +92 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +8 -0
- package/dist/runtime.js.map +1 -0
- package/dist/scenarios.d.ts +125 -0
- package/dist/scenarios.d.ts.map +1 -0
- package/dist/scenarios.js +251 -0
- package/dist/scenarios.js.map +1 -0
- package/package.json +55 -3
package/bin/appOps.mjs
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
// Devoplus One YAYIN ve KULLANIM PANOSU istemcisi (D-86 · D-87). `one publish|promote|rollback|deployments|status`
|
|
2
|
+
// komutları ve Devoplus One MCP sunucusu (`one_publish` · `one_deployments` · `one_app_health`) AYNI modülü kullanır;
|
|
3
|
+
// ikisi aynı backend uçlarını çağırır:
|
|
4
|
+
//
|
|
5
|
+
// POST api/v1/One/apps/{slug}/deployments { portalKey?, action, toVersion? } → 202 { id, status, pipelineUrl? }
|
|
6
|
+
// GET api/v1/One/apps/{slug}/deployments?portalKey= → yayın istekleri
|
|
7
|
+
// GET api/v1/One/apps/{slug}/insights?portalKey=&days= → kullanım ve hata özeti
|
|
8
|
+
//
|
|
9
|
+
// ⛔ Yayın isteği YENİ BİR YETKİ YOLU AÇMAZ: backend yalnız uygulamanın sahibine ya da o portalın şirket yöneticisine
|
|
10
|
+
// izin verir, isteği kayda geçirir ve hattı (D-69) kendisi başlatır; hattın kapıları atlanmaz. Gönderenin kimliği
|
|
11
|
+
// OTURUMDAN (kullanıcının kendi belirteci) gelir; bu modül gövdeye kişi YAZMAZ.
|
|
12
|
+
//
|
|
13
|
+
// ⛔ Kısa ad, portal anahtarı ve sürüm ADRESE girer: biçimleri ağa çıkmadan denetlenir (yol enjeksiyonu yok). Kısa ad ve
|
|
14
|
+
// portal anahtarı kuralları `packages/manifest/src/naming.ts` ile AYNIDIR (eşitlik appOps.test.ts'te kilitli).
|
|
15
|
+
|
|
16
|
+
import { isSecureBase } from './session.mjs'
|
|
17
|
+
|
|
18
|
+
export class AppOpsError extends Error {
|
|
19
|
+
/**
|
|
20
|
+
* @param {string} message kullanıcıya gösterilecek Türkçe metin
|
|
21
|
+
* @param {{ status?: number, code?: string }} [details]
|
|
22
|
+
*/
|
|
23
|
+
constructor(message, details = {}) {
|
|
24
|
+
super(message)
|
|
25
|
+
this.name = 'AppOpsError'
|
|
26
|
+
this.status = details.status
|
|
27
|
+
this.code = details.code
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** `packages/manifest/src/naming.ts` → `SLUG_RE` · `SLUG_MAX` · `PORTAL_KEY_RE` (bin derlemesiz olduğu için kopya; test eşitler). */
|
|
32
|
+
export const SLUG_RE = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/
|
|
33
|
+
export const SLUG_MAX = 24
|
|
34
|
+
export const PORTAL_KEY_RE = /^[a-z0-9]{2,16}$/
|
|
35
|
+
/** Manifest `app.version` biçimi (`packages/manifest/src/schema.ts` → semver). */
|
|
36
|
+
export const VERSION_RE = /^\d+\.\d+\.\d+$/
|
|
37
|
+
|
|
38
|
+
/** Pano gün aralığı: varsayılan 7 (C5). Üst sınır istemcinin makul bir sınırıdır; sunucu daha dar bir sınır uygulayabilir. */
|
|
39
|
+
export const DAYS_DEFAULT = 7
|
|
40
|
+
export const DAYS_MAX = 90
|
|
41
|
+
|
|
42
|
+
/** Yayın eylemleri: tel adı → kullanıcıya görünen ad. */
|
|
43
|
+
export const ACTIONS = {
|
|
44
|
+
deploy: 'Test ortamına yayın (önizleme)',
|
|
45
|
+
promote: 'Bir sonraki ortama terfi',
|
|
46
|
+
rollback: 'Önceki sürüme geri alma',
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Önizleme ortamının (test) uygulama adresi tabanı — mimari §4 "Ortamlar" (D-15). `one publish` her zaman test
|
|
51
|
+
* ortamına yayınlar (D-86); önizleme adresi bu tabandan kurulur.
|
|
52
|
+
*/
|
|
53
|
+
export const PREVIEW_HOST_BASE = 'test-one.devoplus.dev'
|
|
54
|
+
|
|
55
|
+
/** Tanınmayan durum adları olduğu gibi gösterilir (sunucunun yeni durumu uydurma bir etikete çevrilmez). */
|
|
56
|
+
const STATUS_LABELS = {
|
|
57
|
+
queued: 'Sırada',
|
|
58
|
+
not_configured: 'Yayın hattı yapılandırılmamış',
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** `not_configured` yanıtında kullanıcıya söylenecek TEK cümle — fazlası (kime iletildiği, ne zaman açılacağı) bilinmez. */
|
|
62
|
+
export const NOT_CONFIGURED_MESSAGE = 'Yayın hattı bu ortamda yapılandırılmamış; yayın başlatılmadı.'
|
|
63
|
+
export const INSIGHTS_NOT_CONFIGURED_MESSAGE = 'Kullanım ve hata panosu bu ortamda yapılandırılmamış; veri okunamadı.'
|
|
64
|
+
|
|
65
|
+
function clean(value) {
|
|
66
|
+
if (value === undefined || value === null) return undefined
|
|
67
|
+
const text = String(value).trim()
|
|
68
|
+
return text === '' ? undefined : text
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Kısa ad: zorunlu, küçük harfe iner, naming.ts kuralı. */
|
|
72
|
+
export function checkSlug(value) {
|
|
73
|
+
const slug = clean(value)?.toLowerCase()
|
|
74
|
+
if (!slug) throw new AppOpsError('Uygulamanın kısa adı zorunludur (one.manifest.json dosyasındaki app.slug).')
|
|
75
|
+
if (slug.length > SLUG_MAX || !SLUG_RE.test(slug))
|
|
76
|
+
throw new AppOpsError(`Uygulamanın kısa adı geçersiz: "${slug}" (küçük harf, rakam ve tire; en çok ${SLUG_MAX} karakter).`)
|
|
77
|
+
return slug
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Portal anahtarı: isteğe bağlı; verilirse naming.ts kuralı. Verilmezse `undefined` ve istekte YER ALMAZ — hangi
|
|
82
|
+
* kurulumun seçileceğine sunucu karar verir (C5 bunu belirtmiyor; backend tarafına öneri: oturumun portalı).
|
|
83
|
+
*/
|
|
84
|
+
export function checkPortalKey(value) {
|
|
85
|
+
const key = clean(value)?.toLowerCase()
|
|
86
|
+
if (key === undefined) return undefined
|
|
87
|
+
if (!PORTAL_KEY_RE.test(key)) throw new AppOpsError(`Portal anahtarı geçersiz: "${key}" (2-16 küçük harf ya da rakam).`)
|
|
88
|
+
return key
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function checkVersion(value) {
|
|
92
|
+
const version = clean(value)
|
|
93
|
+
if (version === undefined) return undefined
|
|
94
|
+
if (!VERSION_RE.test(version)) throw new AppOpsError(`Sürüm "x.y.z" biçiminde olmalı: "${version}".`)
|
|
95
|
+
return version
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Gün sayısı: verilmezse 7; 1..DAYS_MAX tam sayı. */
|
|
99
|
+
export function checkDays(value) {
|
|
100
|
+
const text = clean(value)
|
|
101
|
+
if (text === undefined) return DAYS_DEFAULT
|
|
102
|
+
const days = Number(text)
|
|
103
|
+
if (!/^\d+$/.test(text) || !Number.isInteger(days) || days < 1 || days > DAYS_MAX)
|
|
104
|
+
throw new AppOpsError(`Gün sayısı 1 ile ${DAYS_MAX} arasında bir tam sayı olmalı: "${text}".`)
|
|
105
|
+
return days
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Yayın isteği gövdesi. `toVersion` yalnız geri almada anlamlıdır; başka eylemde verilirse açık hata (sessizce
|
|
110
|
+
* düşürülseydi kullanıcı belirli bir sürümü yayımladığını sanırdı).
|
|
111
|
+
*/
|
|
112
|
+
export function buildDeploymentRequest(fields = {}) {
|
|
113
|
+
const action = clean(fields.action)
|
|
114
|
+
if (!action || !Object.hasOwn(ACTIONS, action))
|
|
115
|
+
throw new AppOpsError(`Yayın eylemi tanınmadı: "${fields.action ?? ''}". Geçerli eylemler: ${Object.keys(ACTIONS).join(', ')}.`)
|
|
116
|
+
const portalKey = checkPortalKey(fields.portalKey)
|
|
117
|
+
const toVersion = checkVersion(fields.toVersion)
|
|
118
|
+
if (toVersion !== undefined && action !== 'rollback')
|
|
119
|
+
throw new AppOpsError('Sürüm (toVersion) yalnız geri almada verilir.')
|
|
120
|
+
const body = { action }
|
|
121
|
+
if (portalKey !== undefined) body.portalKey = portalKey
|
|
122
|
+
if (toVersion !== undefined) body.toVersion = toVersion
|
|
123
|
+
return body
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** API tabanı `https://…/` biçiminde olmalı (http yalnız yerel adreste); uç yolu ona eklenir. */
|
|
127
|
+
function endpoint(apiBase, path, query = {}) {
|
|
128
|
+
// Belirteç bu adrese gider: düz http yalnız yerel geliştirme adresinde (RFC 6750 §5.3).
|
|
129
|
+
if (!isSecureBase(apiBase))
|
|
130
|
+
throw new AppOpsError(`API tabanı "https://.../" biçiminde ve "/" ile bitmeli (http yalnız localhost): ${apiBase}`)
|
|
131
|
+
const url = new URL(`api/v1/One/apps/${path}`, apiBase)
|
|
132
|
+
for (const [name, value] of Object.entries(query)) if (value !== undefined) url.searchParams.set(name, String(value))
|
|
133
|
+
return url.toString()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Sunucunun ProblemDetails yanıtını kullanıcı metnine çevirir; gövde yoksa durum koduna göre sabit cümle. */
|
|
137
|
+
async function failure(response) {
|
|
138
|
+
let detail
|
|
139
|
+
let code
|
|
140
|
+
try {
|
|
141
|
+
const problem = await response.json()
|
|
142
|
+
detail = typeof problem?.detail === 'string' ? problem.detail : undefined
|
|
143
|
+
code = typeof problem?.code === 'string' ? problem.code : undefined
|
|
144
|
+
} catch {
|
|
145
|
+
// gövdesiz 401/403 — aşağıdaki sabit metin
|
|
146
|
+
}
|
|
147
|
+
const fallback =
|
|
148
|
+
response.status === 401
|
|
149
|
+
? 'Oturumunuz geçersiz ya da süresi dolmuş; yeniden giriş yapın.'
|
|
150
|
+
: response.status === 403
|
|
151
|
+
? 'Bu uygulama için bu işlemi yapma yetkiniz yok.'
|
|
152
|
+
: response.status === 404
|
|
153
|
+
? 'Uygulama ya da kurulum bulunamadı.'
|
|
154
|
+
: response.status === 429
|
|
155
|
+
? 'Çok fazla istek gönderdiniz; bir süre sonra yeniden deneyin.'
|
|
156
|
+
: `Devoplus isteği başarısız oldu (HTTP ${response.status}).`
|
|
157
|
+
return new AppOpsError(detail ?? fallback, { status: response.status, code })
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* @typedef {{ apiBase: string, accessToken: string, fetch?: typeof fetch }} ClientOptions
|
|
162
|
+
*/
|
|
163
|
+
|
|
164
|
+
async function call(options, path, query, init = {}) {
|
|
165
|
+
const doFetch = options.fetch ?? fetch
|
|
166
|
+
if (!options.accessToken) throw new AppOpsError('Oturum yok; önce giriş yapın.', { status: 401 })
|
|
167
|
+
const url = endpoint(options.apiBase, path, query)
|
|
168
|
+
let response
|
|
169
|
+
try {
|
|
170
|
+
response = await doFetch(url, {
|
|
171
|
+
...init,
|
|
172
|
+
headers: {
|
|
173
|
+
Accept: 'application/json',
|
|
174
|
+
Authorization: `Bearer ${options.accessToken}`,
|
|
175
|
+
...(init.body ? { 'Content-Type': 'application/json' } : {}),
|
|
176
|
+
},
|
|
177
|
+
})
|
|
178
|
+
} catch {
|
|
179
|
+
throw new AppOpsError('Devoplus API\'ye ulaşılamadı.')
|
|
180
|
+
}
|
|
181
|
+
if (!response.ok) throw await failure(response)
|
|
182
|
+
try {
|
|
183
|
+
return await response.json()
|
|
184
|
+
} catch {
|
|
185
|
+
throw new AppOpsError('Devoplus API yanıtı okunamadı.', { status: response.status })
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Yayın isteği bırakır (deploy · promote · rollback). Dönüş `{ id, status, pipelineUrl? }`. */
|
|
190
|
+
export function requestDeployment(options, slug, body) {
|
|
191
|
+
const app = checkSlug(slug)
|
|
192
|
+
const request = buildDeploymentRequest(body)
|
|
193
|
+
return call(options, `${encodeURIComponent(app)}/deployments`, {}, { method: 'POST', body: JSON.stringify(request) })
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Uygulamanın (ve verilirse o kurulumun) yayın istekleri. */
|
|
197
|
+
export function listDeployments(options, slug, portalKey) {
|
|
198
|
+
const app = checkSlug(slug)
|
|
199
|
+
return call(options, `${encodeURIComponent(app)}/deployments`, { portalKey: checkPortalKey(portalKey) })
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** Kullanım ve hata özeti. */
|
|
203
|
+
export function getInsights(options, slug, { portalKey, days } = {}) {
|
|
204
|
+
const app = checkSlug(slug)
|
|
205
|
+
return call(options, `${encodeURIComponent(app)}/insights`, { portalKey: checkPortalKey(portalKey), days: checkDays(days) })
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** Önizleme (test ortamı) adresi; portal anahtarı yoksa kurulamaz → `undefined`. */
|
|
209
|
+
export function previewAddress(slug, portalKey) {
|
|
210
|
+
const key = checkPortalKey(portalKey)
|
|
211
|
+
if (key === undefined) return undefined
|
|
212
|
+
return `https://${checkSlug(slug)}-${key}.${PREVIEW_HOST_BASE}`
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function statusLabel(status) {
|
|
216
|
+
return STATUS_LABELS[status] ?? String(status ?? '?')
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Tek bir yayın isteğini tek satıra yazar (CLI listesi ve MCP yanıtı). Bilinmeyen alanlar uydurulmaz. */
|
|
220
|
+
export function describeDeployment(row) {
|
|
221
|
+
const action = ACTIONS[row?.action] ?? (row?.action ? String(row.action) : undefined)
|
|
222
|
+
const parts = [`${row?.id ?? '?'} ${statusLabel(row?.status)}`]
|
|
223
|
+
if (action) parts.push(`, ${action}`)
|
|
224
|
+
if (row?.portalKey) parts.push(`, portal ${row.portalKey}`)
|
|
225
|
+
if (row?.toVersion) parts.push(`, hedef sürüm ${row.toVersion}`)
|
|
226
|
+
if (typeof row?.createdAt === 'string') parts.push(`, ${row.createdAt}`)
|
|
227
|
+
if (typeof row?.pipelineUrl === 'string') parts.push(`\n hat: ${row.pipelineUrl}`)
|
|
228
|
+
return parts.join('')
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Yayın isteği yanıtını kullanıcıya söylenecek metne çevirir. `not_configured` başka bir iddia taşımaz.
|
|
233
|
+
* @param {{ id?: string, status?: string, pipelineUrl?: string }} result
|
|
234
|
+
* @param {{ action: string, slug: string, portalKey?: string }} request
|
|
235
|
+
*/
|
|
236
|
+
export function describeDeploymentResult(result, request) {
|
|
237
|
+
if (result?.status === 'not_configured') return NOT_CONFIGURED_MESSAGE
|
|
238
|
+
const lines = [`Yayın isteği alındı (${ACTIONS[request.action] ?? request.action}): ${describeDeployment({ ...result, action: request.action, portalKey: request.portalKey })}`]
|
|
239
|
+
if (request.action === 'deploy') {
|
|
240
|
+
const preview = previewAddress(request.slug, request.portalKey)
|
|
241
|
+
lines.push(
|
|
242
|
+
preview
|
|
243
|
+
? `Hat bitince önizleme test ortamındadır: test Portal'ından uygulamayı açıp bakabilirsiniz (adres: ${preview}).`
|
|
244
|
+
: 'Hat bitince önizleme test ortamındadır: test Portal\'ından uygulamayı açıp bakabilirsiniz (adres için portal anahtarını verin).',
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
return lines.join('\n')
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function percent(part, whole) {
|
|
251
|
+
if (!Number.isFinite(part) || !Number.isFinite(whole) || whole <= 0) return '?'
|
|
252
|
+
return `%${((part / whole) * 100).toLocaleString('tr-TR', { maximumFractionDigits: 1 })}`
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function number(value) {
|
|
256
|
+
return Number.isFinite(value) ? Number(value).toLocaleString('tr-TR') : '?'
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Kullanım özetini çok satırlı Türkçe metne çevirir. `configured: false` başka veri GÖSTERMEZ. */
|
|
260
|
+
export function describeInsights(insights) {
|
|
261
|
+
if (!insights || insights.configured === false) return INSIGHTS_NOT_CONFIGURED_MESSAGE
|
|
262
|
+
const totals = insights.totals ?? {}
|
|
263
|
+
const lines = [
|
|
264
|
+
`Dönem: ${insights.from ?? '?'} ile ${insights.to ?? '?'} arası`,
|
|
265
|
+
`İstek: ${number(totals.requests)}, hata: ${number(totals.errors)} (${percent(totals.errors, totals.requests)})`,
|
|
266
|
+
`İşlemci süresi (istek başına): ortanca ${number(totals.cpuP50Ms)} ms, en yavaş %1 ${number(totals.cpuP99Ms)} ms`,
|
|
267
|
+
]
|
|
268
|
+
const failed = insights.jobs?.failed
|
|
269
|
+
if (Number.isFinite(failed)) lines.push(`Başarısız arka plan işi: ${number(failed)}`)
|
|
270
|
+
const daily = Array.isArray(insights.daily) ? insights.daily : []
|
|
271
|
+
if (daily.length > 0) {
|
|
272
|
+
lines.push('Günlük:')
|
|
273
|
+
for (const day of daily) lines.push(` ${day?.date ?? '?'} istek ${number(day?.requests)}, hata ${number(day?.errors)}`)
|
|
274
|
+
}
|
|
275
|
+
const errors = Array.isArray(insights.recentErrors) ? insights.recentErrors : []
|
|
276
|
+
if (errors.length > 0) {
|
|
277
|
+
lines.push('Son hatalar:')
|
|
278
|
+
for (const e of errors.slice(0, 20)) {
|
|
279
|
+
const message = String(e?.message ?? '').replace(/\s+/g, ' ').slice(0, 300)
|
|
280
|
+
lines.push(` ${e?.time ?? '?'}${e?.path ? ` ${e.path}` : ''} ${message}`)
|
|
281
|
+
}
|
|
282
|
+
} else lines.push('Son hata kaydı yok.')
|
|
283
|
+
return lines.join('\n')
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* `one <komut>` argümanlarını ayrıştırır (SAF — testlenir). `argv` komut adından SONRAKİ argümanlardır.
|
|
288
|
+
* Bilinmeyen seçenek açık hatadır: yazım yanlışı (`--portl`) sessizce yok sayılsaydı istek yanlış kuruluma giderdi.
|
|
289
|
+
* @param {'publish'|'promote'|'rollback'|'deployments'|'status'} command
|
|
290
|
+
* @param {string[]} argv
|
|
291
|
+
*/
|
|
292
|
+
export function parseOpsArgs(command, argv) {
|
|
293
|
+
const allowed = {
|
|
294
|
+
publish: ['portal', 'app'],
|
|
295
|
+
promote: ['portal', 'app'],
|
|
296
|
+
rollback: ['portal', 'app', 'to'],
|
|
297
|
+
deployments: ['portal', 'app'],
|
|
298
|
+
status: ['portal', 'app', 'days'],
|
|
299
|
+
}[command]
|
|
300
|
+
if (!allowed) throw new AppOpsError(`bilinmeyen komut: ${command}`)
|
|
301
|
+
const flags = { publish: [], promote: [], rollback: ['yes'], deployments: [], status: [] }[command]
|
|
302
|
+
const out = { json: false, yes: false }
|
|
303
|
+
for (let i = 0; i < argv.length; i++) {
|
|
304
|
+
const arg = argv[i]
|
|
305
|
+
if (arg === '--json') {
|
|
306
|
+
out.json = true
|
|
307
|
+
continue
|
|
308
|
+
}
|
|
309
|
+
const name = /^--([a-z-]+)$/.exec(arg)?.[1]
|
|
310
|
+
if (name && flags.includes(name)) {
|
|
311
|
+
out[name] = true
|
|
312
|
+
continue
|
|
313
|
+
}
|
|
314
|
+
if (!name || !allowed.includes(name)) throw new AppOpsError(`one ${command}: tanınmayan argüman "${arg}".`)
|
|
315
|
+
const value = argv[i + 1]
|
|
316
|
+
if (value === undefined || value.startsWith('--')) throw new AppOpsError(`one ${command}: --${name} bir değer ister.`)
|
|
317
|
+
out[name] = value
|
|
318
|
+
i++
|
|
319
|
+
}
|
|
320
|
+
const portalKey = checkPortalKey(out.portal)
|
|
321
|
+
const result = { json: out.json, portalKey, app: out.app }
|
|
322
|
+
if (command === 'rollback') {
|
|
323
|
+
result.toVersion = checkVersion(out.to)
|
|
324
|
+
result.yes = out.yes
|
|
325
|
+
}
|
|
326
|
+
if (command === 'status') result.days = checkDays(out.days)
|
|
327
|
+
return result
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** Komut → yayın eylemi. */
|
|
331
|
+
export const COMMAND_ACTIONS = { publish: 'deploy', promote: 'promote', rollback: 'rollback' }
|
|
332
|
+
|
|
333
|
+
/** Geri almanın uyarı metni: veri geri alınırsa dönülen noktadan sonra yazılan SİLİNİR (D-43, mimari §4d). */
|
|
334
|
+
export const ROLLBACK_WARNING =
|
|
335
|
+
'Geri alma uygulamayı önceki sürüme döndürür; veritabanı da o sürümün şemasına dönmek zorundaysa dönülen noktadan ' +
|
|
336
|
+
'sonra kaydedilen veriler KAYBOLUR. Bu geri alınamaz bir işlemdir.'
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// `one data generate [--check]` — tanımlayıcı veri modeli (D-84). one.data.json → yeni migration (yalnız ekleyen) +
|
|
2
|
+
// src/data/types.ts + migrations/.one-data-snapshot.json. Üretim kuralları SDK'nın `data/ddl.ts` modülündedir (saf,
|
|
3
|
+
// testlenir); bu dosya yalnız dosyaları okur ve yazar.
|
|
4
|
+
//
|
|
5
|
+
// Kural kaynağı: yayımlanmış pakette derlenmiş `dist/data/ddl.js`; çalışma alanında (kaynak varken) `src/data/ddl.ts`
|
|
6
|
+
// — eski bir derleme çıktısı yanlış farka yol açmasın diye kaynak önceliklidir.
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs'
|
|
8
|
+
import { dirname, join } from 'node:path'
|
|
9
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
10
|
+
|
|
11
|
+
export class DataCommandError extends Error {}
|
|
12
|
+
|
|
13
|
+
const HERE = dirname(fileURLToPath(import.meta.url))
|
|
14
|
+
|
|
15
|
+
export async function loadDataTools() {
|
|
16
|
+
const src = join(HERE, '../src/data/ddl.ts')
|
|
17
|
+
const dist = join(HERE, '../dist/data/ddl.js')
|
|
18
|
+
const target = existsSync(src) ? src : dist
|
|
19
|
+
if (!existsSync(target)) throw new DataCommandError('Devoplus One SDK eksik kurulmuş (veri modeli üreticisi bulunamadı); "pnpm install" çalıştırın.')
|
|
20
|
+
return import(pathToFileURL(target).href)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function readOptional(path) {
|
|
24
|
+
try {
|
|
25
|
+
return readFileSync(path, 'utf8')
|
|
26
|
+
} catch (err) {
|
|
27
|
+
if (err && err.code === 'ENOENT') return null
|
|
28
|
+
throw err
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const USAGE = `kullanım: one data generate [--check]
|
|
33
|
+
|
|
34
|
+
one.data.json'daki veri modelinden şunları üretir:
|
|
35
|
+
migrations/NNNN_one_data.sql yalnız ekleyen değişiklik (yeni tablo, kolon, dizin); değişiklik yoksa yazılmaz
|
|
36
|
+
src/data/types.ts kayıt ve girdi tipleri + createCrudHandler'a verilen dataModel
|
|
37
|
+
migrations/.one-data-snapshot.json bir sonraki farkın alındığı model (elle düzenlenmez)
|
|
38
|
+
--check hiçbir şey yazmaz; dosyalar güncel değilse ya da model hatalıysa 1 ile çıkar.
|
|
39
|
+
|
|
40
|
+
Silme, yeniden adlandırma, tür değişikliği, zorunlu/isteğe bağlı değişikliği ve seçim değeri kaldırma üretilmez; komut
|
|
41
|
+
nedenini söyleyip durur. Seçim alanına yeni değer eklemek migration gerektirmez (yalnız tipler yenilenir).`
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Komutu çalıştırır; çıkış kodu döner. `io.log` / `io.error` çıktıyı yazar (testte toplanır).
|
|
45
|
+
* @param {{ cwd: string, args: string[], log?: (s: string) => void, error?: (s: string) => void }} io
|
|
46
|
+
*/
|
|
47
|
+
export async function runDataCommand({ cwd, args, log = console.log, error = console.error }) {
|
|
48
|
+
const [sub, ...rest] = args
|
|
49
|
+
if (!sub || sub === '--help' || sub === '-h') {
|
|
50
|
+
log(USAGE)
|
|
51
|
+
return sub ? 0 : 1
|
|
52
|
+
}
|
|
53
|
+
if (sub !== 'generate') {
|
|
54
|
+
error(`one data: bilinmeyen alt komut "${sub}".\n${USAGE}`)
|
|
55
|
+
return 1
|
|
56
|
+
}
|
|
57
|
+
const unknown = rest.filter((a) => a !== '--check')
|
|
58
|
+
if (unknown.length > 0) {
|
|
59
|
+
error(`one data generate: bilinmeyen seçenek ${unknown.join(' ')}`)
|
|
60
|
+
return 1
|
|
61
|
+
}
|
|
62
|
+
const check = rest.includes('--check')
|
|
63
|
+
const tools = await loadDataTools()
|
|
64
|
+
|
|
65
|
+
const modelRaw = readOptional(join(cwd, tools.DATA_MODEL_FILE))
|
|
66
|
+
if (modelRaw === null) {
|
|
67
|
+
error(`one data: bu dizinde ${tools.DATA_MODEL_FILE} yok. Veri modelini önce bu dosyaya yazın (belge: "Veri modeli ve kayıt işlemleri").`)
|
|
68
|
+
return 1
|
|
69
|
+
}
|
|
70
|
+
let model
|
|
71
|
+
try {
|
|
72
|
+
model = JSON.parse(modelRaw)
|
|
73
|
+
} catch (err) {
|
|
74
|
+
error(`one data: ${tools.DATA_MODEL_FILE} geçerli JSON değil (${err instanceof Error ? err.message : String(err)}).`)
|
|
75
|
+
return 1
|
|
76
|
+
}
|
|
77
|
+
const migrationsDir = join(cwd, 'migrations')
|
|
78
|
+
const existing = existsSync(migrationsDir)
|
|
79
|
+
? readdirSync(migrationsDir)
|
|
80
|
+
.filter((f) => f.endsWith('.sql'))
|
|
81
|
+
.sort()
|
|
82
|
+
.map((name) => ({ name, sql: readFileSync(join(migrationsDir, name), 'utf8') }))
|
|
83
|
+
: []
|
|
84
|
+
const snapshotPath = join(migrationsDir, tools.DATA_SNAPSHOT_FILE)
|
|
85
|
+
let plan
|
|
86
|
+
try {
|
|
87
|
+
plan = tools.planDataGeneration({ model, snapshot: readOptional(snapshotPath), existing })
|
|
88
|
+
} catch (err) {
|
|
89
|
+
if (err instanceof Error && err.name === 'DataModelError') {
|
|
90
|
+
error(`one data: değişiklik üretilmedi.\n${err.message}`)
|
|
91
|
+
return 1
|
|
92
|
+
}
|
|
93
|
+
throw err
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const typesPath = join(cwd, tools.DATA_TYPES_FILE)
|
|
97
|
+
const writes = []
|
|
98
|
+
if (plan.migration) writes.push({ path: join(migrationsDir, plan.migration.name), content: plan.migration.sql, label: `migrations/${plan.migration.name}` })
|
|
99
|
+
if (readOptional(typesPath) !== plan.types) writes.push({ path: typesPath, content: plan.types, label: tools.DATA_TYPES_FILE })
|
|
100
|
+
if (readOptional(snapshotPath) !== plan.snapshot) writes.push({ path: snapshotPath, content: plan.snapshot, label: `migrations/${tools.DATA_SNAPSHOT_FILE}` })
|
|
101
|
+
|
|
102
|
+
const describe = (c) =>
|
|
103
|
+
c.kind === 'create-table'
|
|
104
|
+
? `yeni tablo: ${c.entity}`
|
|
105
|
+
: c.kind === 'add-column'
|
|
106
|
+
? `yeni alan: ${c.entity}.${c.field}`
|
|
107
|
+
: c.kind === 'add-search-column'
|
|
108
|
+
? `arama kolonu: ${c.entity}.${c.field}`
|
|
109
|
+
: `yeni dizin: ${c.entity}.${c.field}`
|
|
110
|
+
for (const c of plan.changes) log(` ${describe(c)}`)
|
|
111
|
+
|
|
112
|
+
if (check) {
|
|
113
|
+
if (writes.length === 0) {
|
|
114
|
+
log('one data: dosyalar güncel.')
|
|
115
|
+
return 0
|
|
116
|
+
}
|
|
117
|
+
log(`one data: güncel değil; "one data generate" şunları yazar:\n${writes.map((w) => ` ${w.label}`).join('\n')}`)
|
|
118
|
+
return 1
|
|
119
|
+
}
|
|
120
|
+
if (writes.length === 0) {
|
|
121
|
+
log('one data: değişiklik yok; dosyalar güncel.')
|
|
122
|
+
return 0
|
|
123
|
+
}
|
|
124
|
+
for (const w of writes) {
|
|
125
|
+
mkdirSync(dirname(w.path), { recursive: true })
|
|
126
|
+
writeFileSync(w.path, w.content)
|
|
127
|
+
log(`yazıldı: ${w.label}`)
|
|
128
|
+
}
|
|
129
|
+
if (plan.migration) log('Sıradaki adım: "one migrate" (yerel veritabanına uygular). Yayında migration platform tarafından uygulanır.')
|
|
130
|
+
return 0
|
|
131
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// `one` komutunun SAF yardımcıları (D-72): manifest → yerel çalışma ortamı yapılandırması + komut argümanları.
|
|
2
|
+
// Ağ ve dosya sistemi YOK → testlenir (devConfig.test.ts). Komutun kendisi one.mjs'tedir.
|
|
3
|
+
//
|
|
4
|
+
// Yerel çalışma ortamı uygulama deposuna yapılandırma dosyası BIRAKMAZ: yapılandırma her komutta manifest'ten
|
|
5
|
+
// `.one/dev.jsonc`'a yeniden üretilir ve yerel veri `.one/state/` altında durur (ikisi de .gitignore'da). Gerçek
|
|
6
|
+
// yüklemede yapılandırmayı one-ci üretir; buradaki adlar ve kimlikler yalnız yereldir.
|
|
7
|
+
|
|
8
|
+
export const DEV_DIR = '.one'
|
|
9
|
+
export const DEV_CONFIG_FILE = 'dev.jsonc'
|
|
10
|
+
export const DEV_STATE_DIR = 'state'
|
|
11
|
+
/** Yerelde Devoplus servislerine giden istekler bu tabana gider (test ortamı); `--api-base` ya da ONE_API_BASE ile değişir. */
|
|
12
|
+
export const DEFAULT_API_BASE = 'https://test-api.devoplus.dev/'
|
|
13
|
+
/** Çalışma ortamı sürüm tarihi — platformun yüklemede kullandığıyla aynı tutulur. */
|
|
14
|
+
export const COMPATIBILITY_DATE = '2026-10-01'
|
|
15
|
+
|
|
16
|
+
const SLUG_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/
|
|
17
|
+
const ENTRY_RE = /^[\w./-]+\.(ts|js|mjs)$/
|
|
18
|
+
const RENAMED = { d1: 'database', r2: 'files', kv: 'cache' }
|
|
19
|
+
|
|
20
|
+
export class OneCliError extends Error {}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Manifest'ten yalnız yerel çalışma için gereken alanları okur. Tam doğrulama one-ci'dedir (`one-ci validate`);
|
|
24
|
+
* burada yalnız komutun yanlış bir şey çalıştırmasına yol açacak hatalar yakalanır.
|
|
25
|
+
*/
|
|
26
|
+
export function readDevManifest(raw) {
|
|
27
|
+
let m
|
|
28
|
+
try {
|
|
29
|
+
m = JSON.parse(raw)
|
|
30
|
+
} catch {
|
|
31
|
+
throw new OneCliError('one.manifest.json okunamadı: geçerli JSON değil.')
|
|
32
|
+
}
|
|
33
|
+
const slug = m?.app?.slug
|
|
34
|
+
if (typeof slug !== 'string' || !SLUG_RE.test(slug)) throw new OneCliError('one.manifest.json: app.slug eksik ya da geçersiz.')
|
|
35
|
+
const entry = m?.entry
|
|
36
|
+
if (typeof entry !== 'string' || !ENTRY_RE.test(entry) || entry.split('/').includes('..'))
|
|
37
|
+
throw new OneCliError('one.manifest.json: entry eksik ya da geçersiz (ör. "src/index.ts").')
|
|
38
|
+
const services = m?.services ?? {}
|
|
39
|
+
for (const [old, next] of Object.entries(RENAMED))
|
|
40
|
+
if (old in services) throw new OneCliError(`one.manifest.json: "services.${old}" kaldırıldı; yerine "services.${next}" yazın.`)
|
|
41
|
+
return {
|
|
42
|
+
slug,
|
|
43
|
+
entry,
|
|
44
|
+
database: services.database !== false,
|
|
45
|
+
files: services.files === true,
|
|
46
|
+
cache: services.cache === true,
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Yerel yapılandırma (`.one/dev.jsonc`). Göreli yollar bu dosyanın dizinine (`.one/`) göredir. */
|
|
51
|
+
export function devConfig(manifest, options = {}) {
|
|
52
|
+
const apiBase = options.apiBase || DEFAULT_API_BASE
|
|
53
|
+
if (!/^https?:\/\/[^\s]+\/$/.test(apiBase)) throw new OneCliError(`API tabanı "http(s)://.../" biçiminde ve "/" ile bitmeli: ${apiBase}`)
|
|
54
|
+
const local = `one-local-${manifest.slug}`
|
|
55
|
+
const config = {
|
|
56
|
+
name: local,
|
|
57
|
+
main: `../${manifest.entry}`,
|
|
58
|
+
compatibility_date: COMPATIBILITY_DATE,
|
|
59
|
+
vars: { API_BASE: apiBase },
|
|
60
|
+
}
|
|
61
|
+
if (manifest.database)
|
|
62
|
+
config.d1_databases = [{ binding: 'DB', database_name: local, database_id: 'local', migrations_dir: '../migrations' }]
|
|
63
|
+
if (manifest.files) config.r2_buckets = [{ binding: 'FILES', bucket_name: local }]
|
|
64
|
+
if (manifest.cache) config.kv_namespaces = [{ binding: 'CACHE', id: 'local' }]
|
|
65
|
+
return config
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const COMMANDS = ['dev', 'migrate', 'build']
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Komut → çalışma ortamı aracının argümanları. `dev` ve `migrate` aynı yerel veri dizinini kullanır; aksi hâlde
|
|
72
|
+
* `one migrate`'in kurduğu tablolar `one dev`'de görünmezdi.
|
|
73
|
+
*/
|
|
74
|
+
export function commandArgs(command, options = {}) {
|
|
75
|
+
const config = `${DEV_DIR}/${DEV_CONFIG_FILE}`
|
|
76
|
+
const state = `${DEV_DIR}/${DEV_STATE_DIR}`
|
|
77
|
+
switch (command) {
|
|
78
|
+
case 'dev': {
|
|
79
|
+
const args = ['dev', '-c', config, '--persist-to', state]
|
|
80
|
+
if (options.port !== undefined) {
|
|
81
|
+
const port = Number(options.port)
|
|
82
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new OneCliError(`--port geçersiz: ${options.port}`)
|
|
83
|
+
args.push('--port', String(port))
|
|
84
|
+
}
|
|
85
|
+
return args
|
|
86
|
+
}
|
|
87
|
+
case 'migrate':
|
|
88
|
+
return ['d1', 'migrations', 'apply', 'DB', '--local', '-c', config, '--persist-to', state]
|
|
89
|
+
case 'build':
|
|
90
|
+
// Çıktı dizini MUTLAK verilir: göreli yol aracın sürümüne göre yapılandırma dizinine ya da çalışma dizinine
|
|
91
|
+
// göre çözülüyor ve çıktı iki yere bölünüyordu (ölçüldü, 17.10.2026).
|
|
92
|
+
if (!options.outDir) throw new OneCliError('build için çıktı dizini verilmedi.')
|
|
93
|
+
return ['deploy', '--dry-run', '--outdir', options.outDir, '-c', config]
|
|
94
|
+
default:
|
|
95
|
+
throw new OneCliError(`bilinmeyen komut: ${command} (${COMMANDS.join(', ')})`)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** `migrate` veritabanı olmayan uygulamada anlamsızdır; açık hata verilir. */
|
|
100
|
+
export function assertCommandFits(command, manifest) {
|
|
101
|
+
if (command === 'migrate' && !manifest.database)
|
|
102
|
+
throw new OneCliError('Bu uygulama veritabanı kullanmıyor (services.database: false); "one migrate" gerekmez.')
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export const USAGE = `kullanım: one <komut> [seçenekler]
|
|
106
|
+
|
|
107
|
+
one dev uygulamayı yerelde çalıştırır (Devoplus One çalışma ortamı)
|
|
108
|
+
--port <n> dinlenecek port
|
|
109
|
+
--api-base <url> Devoplus API tabanı (varsayılan ${DEFAULT_API_BASE}; ONE_API_BASE ile de verilir)
|
|
110
|
+
one migrate migrations/ altındaki SQL dosyalarını yerel veritabanına uygular
|
|
111
|
+
one build yükleme provası: kodu paketler, hiçbir yere göndermez (çıktı .one/dist/)
|
|
112
|
+
one data generate [--check]
|
|
113
|
+
one.data.json veri modelinden migration + src/data/types.ts üretir (yalnız ekleyen değişiklik)
|
|
114
|
+
|
|
115
|
+
one login Devoplus hesabınızla giriş (tarayıcıda kod onayı; portal orada seçilir)
|
|
116
|
+
--env <test|uat|prod> ortam (varsayılan test; ONE_ENV ile de verilir)
|
|
117
|
+
--client-id <kimlik> sabit istemci (verilmezse bu bilgisayar kendini bir kez kaydeder)
|
|
118
|
+
one logout bu bilgisayardaki oturumu siler
|
|
119
|
+
one request <tür> ... yöneticiden eksik izni ister (kurulum, DEXA sorgusu, uygulama izni gibi); "one request" türleri listeler
|
|
120
|
+
one requests [kimlik] taleplerinizi ve durumlarını listeler
|
|
121
|
+
one context oturumdaki portalın adını ve var olan kısa adını (portal anahtarı) gösterir [--json]
|
|
122
|
+
|
|
123
|
+
one publish uygulamayı test ortamına yayınlar (paylaşılabilir önizleme) [--portal <anahtar>]
|
|
124
|
+
one promote sürümü bir sonraki ortama taşır (testten uat'a, sonra canlıya) [--portal <anahtar>]
|
|
125
|
+
one rollback önceki sürüme döner (veri kaybı olabilir; --yes ister) [--portal <anahtar>] [--to <x.y.z>] --yes
|
|
126
|
+
one deployments yayın isteklerinizi ve durumlarını listeler [--portal <anahtar>]
|
|
127
|
+
one status kullanım ve hata özeti [--portal <anahtar>] [--days 7]
|
|
128
|
+
Yayın komutları "one login" oturumuyla çalışır; yayını Devoplus hattı yapar, kapıları atlanmaz.
|
|
129
|
+
--portal verilmezse istek portal anahtarı taşımaz, kurulumu sunucu seçer; --app <kısa ad>, --json
|
|
130
|
+
|
|
131
|
+
Yerel yapılandırma ve veri .one/ altındadır; depoya girmez. Gerçek yükleme Devoplus hattıyla yapılır; bu bilgisayardan
|
|
132
|
+
yükleme yoktur.`
|