sumba 2.7.3 → 2.8.0
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/extend/bajo/intl/en-US.json +7 -0
- package/extend/bajo/intl/id.json +7 -0
- package/extend/bajoCli/applet/create-new-site.js +21 -0
- package/extend/bajoCli/applet/remove-site.js +20 -0
- package/extend/bajoCli/applet.js +1 -0
- package/extend/dobo/fixture/site.json +2 -1
- package/extend/dobo/fixture/team-user.json +2 -1
- package/extend/dobo/fixture/user.json +2 -1
- package/extend/dobo/model/site.json +3 -2
- package/extend/dobo/model/team-user.json +7 -1
- package/extend/dobo/model/team.json +12 -5
- package/extend/dobo/model/user.json +2 -1
- package/index.js +6 -0
- package/lib/create-new-site.js +83 -0
- package/lib/remove-site.js +35 -0
- package/package.json +1 -1
- package/wiki/CHANGES.md +6 -0
- package/extend/bajo/hook/dobo@before-find-one-record.js +0 -8
|
@@ -115,6 +115,13 @@
|
|
|
115
115
|
"statusInactive": "Inactive",
|
|
116
116
|
"siteSetting": "Site Settings",
|
|
117
117
|
"validCountryCodeRequired": "Value must be one of valid country codes",
|
|
118
|
+
"aliasRequired": "Alias is required",
|
|
119
|
+
"aliasOrHOstnameExists%s%s": "A site with alias '%s' or hostname '%s' exists already",
|
|
120
|
+
"aliasNotFound%s": "Alias '%s' is nowhere to be found",
|
|
121
|
+
"writingModel%s%s": "Writing model '%s' (%s record(s))",
|
|
122
|
+
"aboutToDeleteSite%s": "You're about to delete a site with alias '%s'. Are you sure?",
|
|
123
|
+
"aboutToCreateSite%s": "You're about to create a site with alias '%s'. Continue?",
|
|
124
|
+
"removedFrom%s%s": "Removed from '%s' (%s record(s))",
|
|
118
125
|
"field": {
|
|
119
126
|
"currentPassword": "Current Password",
|
|
120
127
|
"newPassword": "New Password",
|
package/extend/bajo/intl/id.json
CHANGED
|
@@ -116,6 +116,13 @@
|
|
|
116
116
|
"statusInactive": "Non Aktif",
|
|
117
117
|
"siteSetting": "Setelan Situs",
|
|
118
118
|
"validCountryCodeRequired": "Nilai harus salah satu dari kode negara yang berlaku",
|
|
119
|
+
"aliasRequired": "Alias tidak boleh kosong",
|
|
120
|
+
"aliasOrHOstnameExists%s%s": "Situs dengan alias '%s' atau nama host '%s' telah ada",
|
|
121
|
+
"aliasNotFound%s": "Alias '%s' tidak ditemukan",
|
|
122
|
+
"writingModel%s%s": "Menulis model '%s' (%s data)",
|
|
123
|
+
"aboutToDeleteSite%s": "Anda akan menghapus sebuah situs dengan alias '%s'. Anda yakin?",
|
|
124
|
+
"aboutToCreateSite%s": "Anda akan membuat sebuah situs dengan alias '%s'. Lanjutkan?",
|
|
125
|
+
"removedFrom%s%s": "Dihapus dari '%s' (%s data)",
|
|
119
126
|
"field": {
|
|
120
127
|
"currentPassword": "Kata Sandi Saat Ini",
|
|
121
128
|
"newPassword": "Kata Sandi Baru",
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
async function createSite (path, ...args) {
|
|
2
|
+
const { createNewSite } = this.app.sumba
|
|
3
|
+
const { importPkg } = this.app.bajo
|
|
4
|
+
const confirm = await importPkg('bajoCli:@inquirer/confirm')
|
|
5
|
+
const [alias, hostname] = args
|
|
6
|
+
if (!alias) this.print.fatal('aliasRequired')
|
|
7
|
+
const answer = await confirm({ message: this.print.buildText('aboutToCreateSite%s', alias), default: false })
|
|
8
|
+
if (!answer) {
|
|
9
|
+
this.print.fail('aborted')
|
|
10
|
+
this.app.exit()
|
|
11
|
+
}
|
|
12
|
+
await this.app.dobo.start()
|
|
13
|
+
try {
|
|
14
|
+
await createNewSite(alias, hostname, true)
|
|
15
|
+
} catch (err) {
|
|
16
|
+
this.print.fatal(err.message)
|
|
17
|
+
}
|
|
18
|
+
this.app.exit()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default createSite
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
async function removeSite (path, ...args) {
|
|
2
|
+
const { importPkg } = this.app.bajo
|
|
3
|
+
const { removeSite } = this.app.sumba
|
|
4
|
+
const confirm = await importPkg('bajoCli:@inquirer/confirm')
|
|
5
|
+
const [alias] = args
|
|
6
|
+
const answer = await confirm({ message: this.print.buildText('aboutToDeleteSite%s', alias), default: false })
|
|
7
|
+
if (!answer) {
|
|
8
|
+
this.print.fail('aborted')
|
|
9
|
+
this.app.exit()
|
|
10
|
+
}
|
|
11
|
+
await this.app.dobo.start()
|
|
12
|
+
try {
|
|
13
|
+
await removeSite(alias, true)
|
|
14
|
+
} catch (err) {
|
|
15
|
+
this.print.fatal(err.message)
|
|
16
|
+
}
|
|
17
|
+
this.app.exit()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default removeSite
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default 'default'
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"name": "alias",
|
|
11
11
|
"type": "string",
|
|
12
12
|
"maxLength": 100,
|
|
13
|
-
"index": "unique"
|
|
13
|
+
"index": "unique",
|
|
14
|
+
"immutable": true
|
|
14
15
|
}, {
|
|
15
16
|
"name": "title",
|
|
16
17
|
"type": "string",
|
|
@@ -31,5 +32,5 @@
|
|
|
31
32
|
"features": ["sumba:personInCharge", "sumba:address", "sumba:social", {
|
|
32
33
|
"name": "sumba:status",
|
|
33
34
|
"default": "ACTIVE"
|
|
34
|
-
}, "dobo:createdAt", "dobo:updatedAt"]
|
|
35
|
+
}, "dobo:createdAt", "dobo:updatedAt", "dobo:immutable"]
|
|
35
36
|
}
|
|
@@ -17,5 +17,11 @@
|
|
|
17
17
|
"fields": ["userId", "siteId", "teamId"],
|
|
18
18
|
"type": "unique"
|
|
19
19
|
}],
|
|
20
|
-
"features": [
|
|
20
|
+
"features": [
|
|
21
|
+
"dobo:createdAt",
|
|
22
|
+
"dobo:updatedAt",
|
|
23
|
+
"sumba:siteId",
|
|
24
|
+
"sumba:userId",
|
|
25
|
+
"dobo:immutable"
|
|
26
|
+
]
|
|
21
27
|
}
|
|
@@ -7,9 +7,16 @@
|
|
|
7
7
|
"fields": ["alias", "siteId"],
|
|
8
8
|
"type": "unique"
|
|
9
9
|
}],
|
|
10
|
-
"features": [
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
|
|
10
|
+
"features": [
|
|
11
|
+
"dobo:createdAt",
|
|
12
|
+
"dobo:updatedAt",
|
|
13
|
+
"dobo:immutable",
|
|
14
|
+
"sumba:siteId",
|
|
15
|
+
{
|
|
16
|
+
"name": "sumba:status",
|
|
17
|
+
"default": "ENABLED",
|
|
18
|
+
"values": ["ENABLED", "DISABLED"]
|
|
19
|
+
},
|
|
20
|
+
"dobo:immutable"
|
|
21
|
+
]
|
|
15
22
|
}
|
package/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
|
+
import createNewSite from './lib/create-new-site.js'
|
|
3
|
+
import removeSite from './lib/remove-site.js'
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Plugin factory
|
|
@@ -127,6 +129,7 @@ async function factory (pkgName) {
|
|
|
127
129
|
}
|
|
128
130
|
}
|
|
129
131
|
this.unsafeUserFields = ['password']
|
|
132
|
+
this.selfBind(['createNewSite', 'removeSite'])
|
|
130
133
|
}
|
|
131
134
|
|
|
132
135
|
init = async () => {
|
|
@@ -574,6 +577,9 @@ async function factory (pkgName) {
|
|
|
574
577
|
if (tpl[1]) payload.text = await render(tpl[1], locals, opts)
|
|
575
578
|
await this.app.masohiMail.send({ payload, source: source ?? this.ns, conn })
|
|
576
579
|
}
|
|
580
|
+
|
|
581
|
+
createNewSite = createNewSite
|
|
582
|
+
removeSite = removeSite
|
|
577
583
|
}
|
|
578
584
|
|
|
579
585
|
return Sumba
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
|
|
3
|
+
async function createNewSite (alias, hostname, verbose) {
|
|
4
|
+
const { getPluginDataDir, readConfig } = this.app.bajo
|
|
5
|
+
const { isEmpty, omit, kebabCase, isString } = this.app.lib._
|
|
6
|
+
const { getModel } = this.app.dobo
|
|
7
|
+
const { fastGlob } = this.app.lib
|
|
8
|
+
|
|
9
|
+
function replaceAlias (alias, items) {
|
|
10
|
+
for (const item of items) {
|
|
11
|
+
for (const key in item) {
|
|
12
|
+
const val = item[key]
|
|
13
|
+
if (isString(val)) item[key] = val.replaceAll('{alias}', alias)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (isEmpty(alias)) throw this.error('aliasRequired%s', alias)
|
|
19
|
+
let spin
|
|
20
|
+
if (verbose) spin = this.print.spinner().start('processing...')
|
|
21
|
+
const formats = this.app.configHandlers.map(item => item.ext.slice(1))
|
|
22
|
+
const overrideBase = `${getPluginDataDir('sumba')}/create-new-site-fixtures/${alias}`
|
|
23
|
+
const models = this.app.dobo.models.filter(m => {
|
|
24
|
+
const prop = m.properties.find(p => p.name === 'siteId')
|
|
25
|
+
return !!prop
|
|
26
|
+
}).map(m => m.name)
|
|
27
|
+
models.unshift('SumbaSite')
|
|
28
|
+
const files = (await fastGlob(`${overrideBase}/*.{${formats.join(',')}}`)).map(file => {
|
|
29
|
+
const ext = path.extname(file)
|
|
30
|
+
return file.slice(0, file.length - ext.length)
|
|
31
|
+
})
|
|
32
|
+
const data = {}
|
|
33
|
+
for (const m of models) {
|
|
34
|
+
const model = getModel(m)
|
|
35
|
+
const file = `${overrideBase}/${kebabCase(m)}`
|
|
36
|
+
let fixtures = (await model.loadFixtures({ collectItems: true, noLookup: true })) ?? []
|
|
37
|
+
if (files.includes(file)) {
|
|
38
|
+
const items = await readConfig(file, { ignoreError: true, defValue: [] })
|
|
39
|
+
if (!isEmpty(items)) fixtures = items
|
|
40
|
+
}
|
|
41
|
+
if (!Array.isArray(fixtures)) fixtures = [fixtures]
|
|
42
|
+
if (fixtures.length === 0) continue
|
|
43
|
+
replaceAlias(alias, fixtures)
|
|
44
|
+
if (isEmpty(alias)) throw this.error('aliasRequired%s', alias)
|
|
45
|
+
const omitted = ['_attachments', 'siteId']
|
|
46
|
+
for (const item of ['createdAt', 'updatedAt', 'immutable']) {
|
|
47
|
+
const props = model.properties.filter(prop => prop.feature === ('dobo:' + item)).map(prop => prop.name)
|
|
48
|
+
if (props.length > 0) omitted.push(...props)
|
|
49
|
+
}
|
|
50
|
+
for (const idx in fixtures) {
|
|
51
|
+
fixtures[idx] = omit(fixtures[idx], omitted)
|
|
52
|
+
}
|
|
53
|
+
data[m] = m === 'SumbaSite' ? fixtures[0] : fixtures
|
|
54
|
+
}
|
|
55
|
+
if (verbose) spin.stop()
|
|
56
|
+
// create site first
|
|
57
|
+
const query = { $or: [{ alias }] }
|
|
58
|
+
if (!isEmpty(hostname)) query.$or.push({ hostname })
|
|
59
|
+
const model = getModel('SumbaSite')
|
|
60
|
+
const site = await model.findOneRecord({ query })
|
|
61
|
+
if (!isEmpty(site)) throw this.error('aliasOrHOstnameExists%s%s', alias, hostname ?? '')
|
|
62
|
+
// lets go
|
|
63
|
+
data.SumbaSite.alias = alias
|
|
64
|
+
if (!isEmpty(hostname)) data.SumbaSite.hostname = hostname
|
|
65
|
+
await model.transaction(async (trx) => {
|
|
66
|
+
const options = { trx }
|
|
67
|
+
const newSite = await model.createRecord(data.SumbaSite, options)
|
|
68
|
+
if (verbose) this.print.succeed('writingModel%s%s', model.name, 1)
|
|
69
|
+
for (const m in data) {
|
|
70
|
+
if (m === 'SumbaSite') continue
|
|
71
|
+
const mdl = getModel(m)
|
|
72
|
+
const fixtures = data[m]
|
|
73
|
+
for (const f of fixtures) {
|
|
74
|
+
f.siteId = newSite.id + ''
|
|
75
|
+
await mdl.createRecord(f, options)
|
|
76
|
+
}
|
|
77
|
+
if (verbose) this.print.succeed('writingModel%s%s', mdl.name, fixtures.length)
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
if (verbose) this.print.info('done')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export default createNewSite
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
async function removeSite (alias, verbose) {
|
|
2
|
+
const { isEmpty, orderBy } = this.app.lib._
|
|
3
|
+
const { getModel } = this.app.dobo
|
|
4
|
+
|
|
5
|
+
if (isEmpty(alias)) throw this.error('aliasRequired%s', alias)
|
|
6
|
+
const model = getModel('SumbaSite')
|
|
7
|
+
const query = { alias }
|
|
8
|
+
const site = await model.findOneRecord({ query })
|
|
9
|
+
if (isEmpty(site)) throw this.error('aliasNotFound%s', alias)
|
|
10
|
+
let spin
|
|
11
|
+
if (verbose) spin = this.print.spinner().start('processing...')
|
|
12
|
+
const models = orderBy(this.app.dobo.models, ['buildLevel'], ['desc']).filter(m => {
|
|
13
|
+
const prop = m.properties.find(p => p.name === 'siteId')
|
|
14
|
+
return !!prop
|
|
15
|
+
}).map(m => m.name)
|
|
16
|
+
if (verbose) spin.stop()
|
|
17
|
+
await model.transaction(async (trx) => {
|
|
18
|
+
const options = { trx }
|
|
19
|
+
for (const m of models) {
|
|
20
|
+
const mdl = getModel(m)
|
|
21
|
+
const ids = (await mdl.findAllRecord({ query: { siteId: site.id } }, options)).map(item => item.id)
|
|
22
|
+
// TODO: backup
|
|
23
|
+
if (ids.length === 0) continue
|
|
24
|
+
for (const id of ids) {
|
|
25
|
+
await mdl.removeRecord(id, { noReturn: true, ...options })
|
|
26
|
+
}
|
|
27
|
+
if (verbose) this.print.succeed('removedFrom%s%s', mdl.name, ids.length)
|
|
28
|
+
}
|
|
29
|
+
await model.removeRecord(site.id, { noReturn: true, ...options })
|
|
30
|
+
if (verbose) this.print.succeed('removedFrom%s%s', model.name, 1)
|
|
31
|
+
})
|
|
32
|
+
if (verbose) this.print.info('done')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default removeSite
|
package/package.json
CHANGED
package/wiki/CHANGES.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-03-11
|
|
4
|
+
|
|
5
|
+
- [2.8.0] Add ```createNewSite()``` and ```applet.crateNewSite```
|
|
6
|
+
- [2.8.0] Add ```removeSite()``` and ```applet.removeSite```
|
|
7
|
+
- [2.8.0] Set ```site.json```, ```user.json```, ```team.json```, ```team-user.json``` first fixture as immutable row
|
|
8
|
+
|
|
3
9
|
## 2026-03-08
|
|
4
10
|
|
|
5
11
|
- [2.7.3] Bug fix on ```mergeTeam()```
|