sumba 2.16.0 → 2.17.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 +4 -0
- package/extend/bajo/intl/id.json +4 -0
- package/extend/dobo/feature/country.js +3 -3
- package/extend/dobo/feature/email.js +2 -2
- package/extend/dobo/feature/lat-lng.js +8 -8
- package/extend/dobo/feature/lat.js +2 -2
- package/extend/dobo/feature/lng.js +2 -2
- package/extend/dobo/feature/phone.js +2 -2
- package/extend/dobo/feature/site-id.js +1 -1
- package/extend/dobo/feature/slug.js +14 -14
- package/extend/dobo/feature/status.js +7 -4
- package/extend/dobo/feature/team-id.js +4 -2
- package/extend/dobo/feature/ts.js +2 -2
- package/extend/dobo/feature/url.js +4 -4
- package/extend/dobo/feature/user-id.js +3 -1
- package/extend/dobo/model/ticket.json +14 -1
- package/extend/waibuDb/schema/site.js +1 -6
- package/extend/waibuDb/schema/team-user.js +3 -25
- package/extend/waibuDb/schema/ticket.json +16 -0
- package/extend/waibuRestApi/route/manage/ticket-cat/model-builder.json +4 -0
- package/lib/util.js +2 -2
- package/package.json +1 -1
- package/wiki/CHANGES.md +5 -0
|
@@ -131,6 +131,10 @@
|
|
|
131
131
|
"protectedArea": "Protected Area",
|
|
132
132
|
"pleaseAuthenticate": "Please authenticate yourself, thank you!",
|
|
133
133
|
"manageAllSite": "Manage All Sites",
|
|
134
|
+
"statusEnabled": "Enabled",
|
|
135
|
+
"statusDisabled": "Disabled",
|
|
136
|
+
"statusOpen": "Open",
|
|
137
|
+
"statusClosed": "Closed",
|
|
134
138
|
"field": {
|
|
135
139
|
"currentPassword": "Current Password",
|
|
136
140
|
"newPassword": "New Password",
|
package/extend/bajo/intl/id.json
CHANGED
|
@@ -132,6 +132,10 @@
|
|
|
132
132
|
"protectedArea": "Wilayah Dilindungi",
|
|
133
133
|
"pleaseAuthenticate": "Silahkan melakukan otentikasi terlebih dahulu, terima kasih!",
|
|
134
134
|
"manageAllSite": "Kelola Semua Situs",
|
|
135
|
+
"statusEnabled": "Dihidupkan",
|
|
136
|
+
"statusDisabled": "Dimatikan",
|
|
137
|
+
"statusOpen": "Terbuka",
|
|
138
|
+
"statusClosed": "Tertutup",
|
|
135
139
|
"field": {
|
|
136
140
|
"currentPassword": "Kata Sandi Saat Ini",
|
|
137
141
|
"newPassword": "Kata Sandi Baru",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
async function country (opts = {}) {
|
|
2
|
-
opts.
|
|
2
|
+
opts.field = opts.field ?? 'country'
|
|
3
3
|
return {
|
|
4
4
|
properties: [{
|
|
5
|
-
name: opts.
|
|
5
|
+
name: opts.field,
|
|
6
6
|
type: 'string',
|
|
7
7
|
maxLength: 2,
|
|
8
8
|
index: opts.index ?? true,
|
|
@@ -10,7 +10,7 @@ async function country (opts = {}) {
|
|
|
10
10
|
rules: ['uppercase', { rule: 'length', params: 2 }],
|
|
11
11
|
rulesMsg: { 'any.only': 'validCountryCodeRequired' }
|
|
12
12
|
}],
|
|
13
|
-
rules: [{ rule: 'trim', fields: [opts.
|
|
13
|
+
rules: [{ rule: 'trim', fields: [opts.field] }]
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
async function email (opts = {}) {
|
|
2
|
-
opts.
|
|
2
|
+
opts.field = opts.field ?? 'email'
|
|
3
3
|
return {
|
|
4
4
|
properties: [{
|
|
5
|
-
name: opts.
|
|
5
|
+
name: opts.field ?? 'email',
|
|
6
6
|
type: 'string',
|
|
7
7
|
maxLength: 50,
|
|
8
8
|
rules: ['email']
|
|
@@ -2,20 +2,20 @@ import { latLngHook } from '../../../lib/util.js'
|
|
|
2
2
|
|
|
3
3
|
async function latLng (opts = {}) {
|
|
4
4
|
const { merge } = this.app.lib._
|
|
5
|
-
opts.
|
|
6
|
-
opts.
|
|
5
|
+
opts.fieldLat = opts.fieldLat ?? 'lat'
|
|
6
|
+
opts.fieldLng = opts.fieldLng ?? 'lng'
|
|
7
7
|
opts.scale = opts.scale ?? 5
|
|
8
8
|
opts.precision = opts.precision ?? 8
|
|
9
9
|
return {
|
|
10
10
|
properties: [{
|
|
11
|
-
name: opts.
|
|
11
|
+
name: opts.fieldLat,
|
|
12
12
|
type: 'double',
|
|
13
13
|
required: opts.required ?? true,
|
|
14
14
|
index: opts.required ?? true,
|
|
15
15
|
precision: opts.precision,
|
|
16
16
|
scale: opts.scale
|
|
17
17
|
}, {
|
|
18
|
-
name: opts.
|
|
18
|
+
name: opts.fieldLng,
|
|
19
19
|
type: 'double',
|
|
20
20
|
required: opts.required ?? true,
|
|
21
21
|
index: opts.required ?? true,
|
|
@@ -24,12 +24,12 @@ async function latLng (opts = {}) {
|
|
|
24
24
|
}],
|
|
25
25
|
hook: {
|
|
26
26
|
beforeCreate: async function (body) {
|
|
27
|
-
await latLngHook.call(this, body, merge({}, opts, { lat: opts.
|
|
28
|
-
await latLngHook.call(this, body, merge({}, opts, { lng: opts.
|
|
27
|
+
await latLngHook.call(this, body, merge({}, opts, { lat: opts.fieldLat }))
|
|
28
|
+
await latLngHook.call(this, body, merge({}, opts, { lng: opts.fieldLng }))
|
|
29
29
|
},
|
|
30
30
|
beforeUpdate: async function (body) {
|
|
31
|
-
await latLngHook.call(this, body, merge({}, opts, { lat: opts.
|
|
32
|
-
await latLngHook.call(this, body, merge({}, opts, { lng: opts.
|
|
31
|
+
await latLngHook.call(this, body, merge({}, opts, { lat: opts.fieldLat }))
|
|
32
|
+
await latLngHook.call(this, body, merge({}, opts, { lng: opts.fieldLng }))
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { latLngHook } from '../../../lib/util.js'
|
|
2
2
|
|
|
3
3
|
async function lat (opts = {}) {
|
|
4
|
-
opts.
|
|
4
|
+
opts.field = opts.field ?? 'lat'
|
|
5
5
|
opts.scale = opts.scale ?? 5
|
|
6
6
|
opts.precision = opts.precision ?? 8
|
|
7
7
|
return {
|
|
8
8
|
properties: [{
|
|
9
|
-
name: opts.
|
|
9
|
+
name: opts.field,
|
|
10
10
|
type: 'double',
|
|
11
11
|
required: opts.required ?? true,
|
|
12
12
|
index: opts.required ?? true,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { latLngHook } from '../../../lib/util.js'
|
|
2
2
|
|
|
3
3
|
async function lng (opts = {}) {
|
|
4
|
-
opts.
|
|
4
|
+
opts.field = opts.field ?? 'lng'
|
|
5
5
|
opts.scale = opts.scale ?? 5
|
|
6
6
|
opts.precision = opts.precision ?? 8
|
|
7
7
|
return {
|
|
8
8
|
properties: [{
|
|
9
|
-
name: opts.
|
|
9
|
+
name: opts.field,
|
|
10
10
|
type: 'double',
|
|
11
11
|
required: opts.required ?? true,
|
|
12
12
|
index: opts.required ?? true,
|
|
@@ -2,36 +2,36 @@ import slug from 'slug'
|
|
|
2
2
|
|
|
3
3
|
async function autoInc (body, opts) {
|
|
4
4
|
const { set, last } = this.app.lib._
|
|
5
|
-
const query = set({}, opts.
|
|
6
|
-
const sort = set({}, opts.
|
|
5
|
+
const query = set({}, opts.field, { $regex: new RegExp('^' + body[opts.field]) })
|
|
6
|
+
const sort = set({}, opts.field, -1)
|
|
7
7
|
const options = { noHook: true, skipCache: true, thrownNotFound: false }
|
|
8
8
|
const resp = await this.findOneRecord({ query, sort }, options)
|
|
9
|
-
if (resp) return body[opts.
|
|
10
|
-
const rslugs = resp[opts.
|
|
11
|
-
const slugs = body[opts.
|
|
9
|
+
if (resp) return body[opts.field]
|
|
10
|
+
const rslugs = resp[opts.field].split('-')
|
|
11
|
+
const slugs = body[opts.field].split('-')
|
|
12
12
|
let num
|
|
13
|
-
if (Number(last(rslugs)) && body[opts.
|
|
13
|
+
if (Number(last(rslugs)) && body[opts.field] === rslugs.slice(0, rslugs.length - 1).join('-')) {
|
|
14
14
|
num = Number(rslugs.pop()) + 1
|
|
15
|
-
body[opts.
|
|
15
|
+
body[opts.field] = `${rslugs.join('-')}-${num}`
|
|
16
16
|
} else {
|
|
17
17
|
const idx = slugs.length - 1
|
|
18
18
|
num = Number(slugs[idx])
|
|
19
|
-
if (!num) body[opts.
|
|
19
|
+
if (!num) body[opts.field] += '-1'
|
|
20
20
|
else {
|
|
21
21
|
slugs[idx] = num + 1
|
|
22
|
-
body[opts.
|
|
22
|
+
body[opts.field] = slugs.join('-')
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
return await autoInc.call(this, body, opts)
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
async function mainFn (opts = {}) {
|
|
29
|
-
opts.
|
|
29
|
+
opts.field = opts.field ?? 'slug'
|
|
30
30
|
opts.fieldSource = opts.fieldSource ?? ['name']
|
|
31
31
|
opts.autoInc = true
|
|
32
32
|
return {
|
|
33
33
|
properties: [{
|
|
34
|
-
name: opts.
|
|
34
|
+
name: opts.field ?? 'slug',
|
|
35
35
|
type: 'string',
|
|
36
36
|
maxLength: 255,
|
|
37
37
|
index: 'unique'
|
|
@@ -40,7 +40,7 @@ async function mainFn (opts = {}) {
|
|
|
40
40
|
beforeCreate: async function (body) {
|
|
41
41
|
const { error } = this.app.bajo
|
|
42
42
|
const { isEmpty, isString } = this.app.lib._
|
|
43
|
-
if (isEmpty(body[opts.
|
|
43
|
+
if (isEmpty(body[opts.field])) {
|
|
44
44
|
if (isString(opts.fieldSource)) opts.fieldSource = [opts.fieldSource]
|
|
45
45
|
const source = []
|
|
46
46
|
opts.fieldSource.forEach(s => {
|
|
@@ -50,9 +50,9 @@ async function mainFn (opts = {}) {
|
|
|
50
50
|
const details = [{ field: opts.fieldSource.join(', '), error: 'required' }]
|
|
51
51
|
throw error('\'%s\' is required', opts.fieldSource.join(', '), { details })
|
|
52
52
|
}
|
|
53
|
-
body[opts.
|
|
53
|
+
body[opts.field] = slug(source.join(' '))
|
|
54
54
|
}
|
|
55
|
-
if (opts.autoInc) body[opts.
|
|
55
|
+
if (opts.autoInc) body[opts.field] = await autoInc.call(this, body, opts)
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
}
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
async function status (opts = {}) {
|
|
2
|
-
opts.
|
|
2
|
+
opts.field = opts.field ?? 'status'
|
|
3
|
+
opts.required = opts.required ?? true
|
|
4
|
+
opts.values = opts.values ?? ['UNVERIFIED', 'ACTIVE', 'INACTIVE']
|
|
3
5
|
return {
|
|
4
6
|
properties: [{
|
|
5
|
-
name: opts.
|
|
7
|
+
name: opts.field ?? 'status',
|
|
6
8
|
type: 'string',
|
|
7
9
|
maxLength: 50,
|
|
8
10
|
index: true,
|
|
9
|
-
|
|
11
|
+
required: opts.required,
|
|
12
|
+
values: opts.values
|
|
10
13
|
}],
|
|
11
14
|
hook: {
|
|
12
15
|
beforeCreate: async function (body) {
|
|
13
16
|
const { isSet } = this.app.lib.aneka
|
|
14
|
-
if (!isSet(body[opts.
|
|
17
|
+
if (!isSet(body[opts.field])) body[opts.field] = opts.default
|
|
15
18
|
}
|
|
16
19
|
}
|
|
17
20
|
}
|
|
@@ -8,14 +8,16 @@ async function teamId (opts = {}) {
|
|
|
8
8
|
ref: {
|
|
9
9
|
site: {
|
|
10
10
|
model: 'SumbaSite',
|
|
11
|
-
|
|
11
|
+
field: 'id',
|
|
12
12
|
type: '1:1',
|
|
13
13
|
fields: ['id', 'alias', 'hostname', 'title']
|
|
14
14
|
},
|
|
15
15
|
team: {
|
|
16
16
|
model: 'SumbaTeam',
|
|
17
|
-
|
|
17
|
+
field: 'id',
|
|
18
18
|
type: '1:1',
|
|
19
|
+
labelField: 'name',
|
|
20
|
+
searchField: 'name',
|
|
19
21
|
fields: ['id', 'name']
|
|
20
22
|
}
|
|
21
23
|
},
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
async function ts (opts = {}) {
|
|
2
|
-
opts.
|
|
2
|
+
opts.field = opts.field ?? 'ts'
|
|
3
3
|
return {
|
|
4
4
|
properties: [{
|
|
5
|
-
name: opts.
|
|
5
|
+
name: opts.field ?? 'ts',
|
|
6
6
|
type: 'timestamp',
|
|
7
7
|
required: opts.required ?? true,
|
|
8
8
|
index: opts.index ?? true
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
async function hook (body, options) {
|
|
2
2
|
const { isSet } = this.app.lib.aneka
|
|
3
|
-
let val = body[options.
|
|
3
|
+
let val = body[options.field]
|
|
4
4
|
if (!isSet(val)) return
|
|
5
5
|
const [, ...params] = val.split('://')
|
|
6
6
|
if (params.length === 0) val = options.defProto + '://' + val
|
|
7
|
-
body[options.
|
|
7
|
+
body[options.field] = val
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
async function url (opts = {}) {
|
|
11
|
-
opts.
|
|
11
|
+
opts.field = opts.field ?? 'url'
|
|
12
12
|
opts.defProto = opts.defProto ?? 'http'
|
|
13
13
|
return {
|
|
14
14
|
properties: [{
|
|
15
|
-
name: opts.
|
|
15
|
+
name: opts.field ?? 'url',
|
|
16
16
|
type: 'string'
|
|
17
17
|
}],
|
|
18
18
|
hook: {
|
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"properties": [
|
|
3
3
|
"subject,,255,true,true",
|
|
4
|
-
|
|
4
|
+
{
|
|
5
|
+
"name": "cat",
|
|
6
|
+
"type": "string",
|
|
7
|
+
"maxLength": 50,
|
|
8
|
+
"index": true,
|
|
9
|
+
"required": true,
|
|
10
|
+
"ref": {
|
|
11
|
+
"cat": {
|
|
12
|
+
"model": "SumbaTicketCat",
|
|
13
|
+
"searchField": "name",
|
|
14
|
+
"labelField": "name"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
},
|
|
5
18
|
"message,text,,,true"
|
|
6
19
|
],
|
|
7
20
|
"features": [
|
|
@@ -28,12 +28,7 @@ async function site (req = {}) {
|
|
|
28
28
|
{ name: 'personInCharge', fields: ['picName:3-md 6-sm:Name', 'picRole:3-md 6-sm:Role', 'picPhone:3-md 6-sm:Phone', 'picEmail:3-md 6-sm:Email'] },
|
|
29
29
|
{ name: 'address', fields: ['address1:12', 'address2:12', 'city:6-md 8-sm', 'zipCode:2-md 4-sm', 'provinceState:4-md', 'country:6-md', 'phone:6-md', 'website:12'] },
|
|
30
30
|
{ name: 'socialMedia', fields: ['socX:3-md 6-sm', 'socInstagram:3-md 6-sm', 'socFacebook:3-md 6-sm', 'socLinkedIn:3-md 6-sm'] }
|
|
31
|
-
]
|
|
32
|
-
widget: {
|
|
33
|
-
country: {
|
|
34
|
-
component: 'form-select-ext'
|
|
35
|
-
}
|
|
36
|
-
}
|
|
31
|
+
]
|
|
37
32
|
},
|
|
38
33
|
view: {
|
|
39
34
|
details,
|
|
@@ -5,44 +5,22 @@ async function teamUser () {
|
|
|
5
5
|
{ name: 'meta', fields: ['id', 'createdAt', 'updatedAt'] },
|
|
6
6
|
{ name: 'general', fields: ['userId:6-md', 'teamId:6-md'] }
|
|
7
7
|
],
|
|
8
|
-
calcFields: [
|
|
9
|
-
{ name: 'user', type: 'string' },
|
|
10
|
-
{ name: 'team', type: 'string' }
|
|
11
|
-
],
|
|
12
|
-
valueFormatter: {
|
|
13
|
-
user: (val, rec) => {
|
|
14
|
-
return rec._ref.user.username
|
|
15
|
-
},
|
|
16
|
-
team: (val, rec) => {
|
|
17
|
-
return rec._ref.team.name
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
8
|
widget: {
|
|
21
9
|
userId: {
|
|
22
|
-
component: 'form-select-ext',
|
|
23
10
|
attr: {
|
|
24
|
-
|
|
25
|
-
remoteSearchField: 'username',
|
|
26
|
-
remoteLabelField: 'username',
|
|
27
|
-
remoteApiKey: true,
|
|
28
|
-
ref: 'user:username'
|
|
11
|
+
url: 'sumba.restapi:/manage/user'
|
|
29
12
|
}
|
|
30
13
|
},
|
|
31
14
|
teamId: {
|
|
32
|
-
component: 'form-select-ext',
|
|
33
15
|
attr: {
|
|
34
|
-
|
|
35
|
-
remoteSearchField: 'name',
|
|
36
|
-
remoteLabelField: 'name',
|
|
37
|
-
remoteApiKey: true,
|
|
38
|
-
ref: 'team:name'
|
|
16
|
+
url: 'sumba.restapi:/manage/team'
|
|
39
17
|
}
|
|
40
18
|
}
|
|
41
19
|
}
|
|
42
20
|
},
|
|
43
21
|
view: {
|
|
44
22
|
list: {
|
|
45
|
-
fields: ['
|
|
23
|
+
fields: ['userId', 'teamId', 'createdAt', 'updatedAt'],
|
|
46
24
|
stat: {
|
|
47
25
|
aggregate: [
|
|
48
26
|
{ fields: ['userId'], group: 'userId', aggregate: ['count'] },
|
package/lib/util.js
CHANGED
|
@@ -178,8 +178,8 @@ export async function checkUserId (req, reply, source) {
|
|
|
178
178
|
export async function latLngHook (body, options) {
|
|
179
179
|
const { isSet } = this.app.lib.aneka
|
|
180
180
|
const { round } = this.app.lib.aneka
|
|
181
|
-
if (!isSet(body[options.
|
|
182
|
-
body[options.
|
|
181
|
+
if (!isSet(body[options.field])) return
|
|
182
|
+
body[options.field] = round(body[options.field], options.scale)
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
export async function checkinterSite (req, reply) {
|
package/package.json
CHANGED
package/wiki/CHANGES.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-04-07
|
|
4
|
+
|
|
5
|
+
- [2.17.0] Change all ```opts.fieldName``` to ```opts.field``` in features
|
|
6
|
+
- [2.17.0] Rewrite necessary changes on model reference to match the new architecture
|
|
7
|
+
|
|
3
8
|
## 2026-04-02
|
|
4
9
|
|
|
5
10
|
- [2.16.0] Add ```SumbaUserSetting``` model and necessary handlings
|