Skip to content

Commit b9ce9d3

Browse files
committed
feat: Implement file-based data storage for mock server entities and add initial mock data.
1 parent bf0be96 commit b9ce9d3

8 files changed

Lines changed: 350 additions & 89 deletions

File tree

mockServer/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
"scripts": {
2525
"start": "gulp nodemon",
2626
"dev": "gulp",
27+
"dev:file": "MOCK_DB_MODE=file gulp",
28+
"export-db-to-file": "node scripts/export-db-to-file.js",
2729
"build:js": "babel src --out-dir dist",
2830
"build:static": "ncp src/assets dist/assets && ncp src/mock dist/mock && ncp src/database dist/database",
2931
"build": "npm run build:js && npm run build:static",
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
#!/usr/bin/env node
2+
3+
const fs = require('fs')
4+
const path = require('path')
5+
6+
const sourceDir = path.resolve(__dirname, '../src/database')
7+
const outputDir = process.env.MOCK_FILE_DB_PATH || path.resolve(__dirname, '../data')
8+
const forceOverwrite = process.argv.includes('--force')
9+
10+
const collectionNamingFields = {
11+
pages: ['name'],
12+
apps: ['name'],
13+
blocks: ['label', 'name'],
14+
blockGroups: ['name'],
15+
blockCategories: ['name']
16+
}
17+
18+
function sanitizeFileName (name) {
19+
if (!name) {
20+
return ''
21+
}
22+
23+
const normalized = String(name)
24+
.trim()
25+
.replace(/\s+/g, '-')
26+
.replace(/[<>:"/\\|?*]/g, '-')
27+
.replace(/-+/g, '-')
28+
.replace(/^\.+/, '')
29+
.replace(/\.+$/, '')
30+
.slice(0, 120)
31+
32+
if (!normalized || normalized === '.' || normalized === '..') {
33+
return ''
34+
}
35+
36+
return normalized
37+
}
38+
39+
function resolveFileBaseName (doc, collectionName, usedNames) {
40+
const namingFields = collectionNamingFields[collectionName] || ['name', 'label']
41+
let preferred = ''
42+
43+
for (const field of namingFields) {
44+
const value = sanitizeFileName(doc[field])
45+
if (value) {
46+
preferred = value
47+
break
48+
}
49+
}
50+
51+
if (!preferred) {
52+
preferred = sanitizeFileName(doc.id) || sanitizeFileName(doc._id) || 'record'
53+
}
54+
55+
const idSuffix = String(doc._id || doc.id || 'item').slice(0, 6)
56+
const normalizedPreferred = preferred.toLowerCase()
57+
if (!usedNames.has(normalizedPreferred)) {
58+
usedNames.add(normalizedPreferred)
59+
return preferred
60+
}
61+
62+
let attempt = `${preferred}-${idSuffix}`
63+
let seq = 2
64+
65+
while (usedNames.has(attempt.toLowerCase())) {
66+
attempt = `${preferred}-${idSuffix}-${seq}`
67+
seq += 1
68+
}
69+
70+
usedNames.add(attempt.toLowerCase())
71+
return attempt
72+
}
73+
74+
function parseDbFile (dbPath) {
75+
const content = fs.readFileSync(dbPath, 'utf8')
76+
return content
77+
.split('\n')
78+
.map((line) => line.trim())
79+
.filter(Boolean)
80+
.map((line, index) => {
81+
try {
82+
return JSON.parse(line)
83+
} catch (error) {
84+
throw new Error(`Failed to parse ${path.basename(dbPath)} line ${index + 1}: ${error.message}`)
85+
}
86+
})
87+
}
88+
89+
function ensureDirectory (dirPath) {
90+
fs.mkdirSync(dirPath, { recursive: true })
91+
}
92+
93+
function cleanCollectionDirectory (collectionPath) {
94+
if (!fs.existsSync(collectionPath)) {
95+
return
96+
}
97+
98+
const files = fs.readdirSync(collectionPath)
99+
for (const fileName of files) {
100+
if (fileName.endsWith('.json')) {
101+
fs.unlinkSync(path.join(collectionPath, fileName))
102+
}
103+
}
104+
}
105+
106+
function exportCollection (dbFile) {
107+
const collectionName = path.basename(dbFile, '.db')
108+
const dbPath = path.join(sourceDir, dbFile)
109+
const collectionPath = path.join(outputDir, collectionName)
110+
const docs = parseDbFile(dbPath)
111+
112+
ensureDirectory(collectionPath)
113+
if (forceOverwrite) {
114+
cleanCollectionDirectory(collectionPath)
115+
}
116+
117+
let written = 0
118+
let skipped = 0
119+
const usedNames = new Set()
120+
121+
for (const doc of docs) {
122+
if (!doc._id && doc.id !== undefined) {
123+
doc._id = String(doc.id)
124+
}
125+
126+
const fileBaseName = resolveFileBaseName(doc, collectionName, usedNames)
127+
const filePath = path.join(collectionPath, `${fileBaseName}.json`)
128+
129+
if (!forceOverwrite && fs.existsSync(filePath)) {
130+
skipped += 1
131+
continue
132+
}
133+
134+
fs.writeFileSync(filePath, JSON.stringify(doc, null, 2), 'utf8')
135+
written += 1
136+
}
137+
138+
return { collectionName, total: docs.length, written, skipped }
139+
}
140+
141+
function run () {
142+
if (!fs.existsSync(sourceDir)) {
143+
throw new Error(`Database directory not found: ${sourceDir}`)
144+
}
145+
146+
ensureDirectory(outputDir)
147+
148+
const dbFiles = fs.readdirSync(sourceDir).filter((fileName) => fileName.endsWith('.db'))
149+
150+
if (dbFiles.length === 0) {
151+
console.log('No .db files found, nothing to export.')
152+
return
153+
}
154+
155+
const summary = dbFiles.map(exportCollection)
156+
157+
console.log(`Export complete. Output: ${outputDir}`)
158+
summary.forEach((item) => {
159+
console.log(
160+
`- ${item.collectionName}: total=${item.total}, written=${item.written}, skipped=${item.skipped}`
161+
)
162+
})
163+
}
164+
165+
try {
166+
run()
167+
} catch (error) {
168+
console.error(error.message)
169+
process.exit(1)
170+
}

mockServer/src/services/apps.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,13 @@ const defaultApp = {
6464
export default class AppsService {
6565
constructor() {
6666
this.store = createStore('apps', {
67-
indexes: [{ fieldName: '_id', unique: true }]
67+
indexes: [{ fieldName: '_id', unique: true }],
68+
namingFields: ['name']
6869
})
6970

7071
this.schemaStore = createStore('appsSchema', {
71-
indexes: [{ fieldName: '_id', unique: true }]
72+
indexes: [{ fieldName: '_id', unique: true }],
73+
namingFields: ['id']
7274
})
7375

7476
this.appList = []

mockServer/src/services/block.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import { getResponseData } from '../tool/Common'
1515
export default class BlockService {
1616
constructor() {
1717
this.store = createStore('blocks', {
18-
indexes: [{ fieldName: 'label', unique: true }]
18+
indexes: [{ fieldName: 'label', unique: true }],
19+
namingFields: ['label', 'name']
1920
})
2021

2122
this.userInfo = {

mockServer/src/services/blockCategory.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import appinfo from '../assets/json/appinfo.json'
1616
export default class BlockCategoryService {
1717
constructor() {
1818
this.store = createStore('blockCategories', {
19-
indexes: [{ fieldName: 'name', unique: true }]
19+
indexes: [{ fieldName: 'name', unique: true }],
20+
namingFields: ['name']
2021
})
2122

2223
this.blockCategoriesModel = {

mockServer/src/services/blockGroup.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import appinfo from '../assets/json/appinfo.json'
1616
export default class BlockGroupService {
1717
constructor() {
1818
this.store = createStore('blockGroups', {
19-
indexes: [{ fieldName: 'name', unique: true }]
19+
indexes: [{ fieldName: 'name', unique: true }],
20+
namingFields: ['name']
2021
})
2122

2223
this.blockGroupModel = {

mockServer/src/services/pages.js

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
*/
1212

1313
import createStore from '../store/StoreFactory'
14+
import config from '../config/config'
1415
import { getResponseData } from '../tool/Common'
1516

1617
const parsePageContent = (item) => {
@@ -24,10 +25,34 @@ const parsePageContent = (item) => {
2425
return item
2526
}
2627

28+
const formatPageContentForStorage = (pageContent) => {
29+
if (pageContent === undefined) {
30+
return pageContent
31+
}
32+
33+
if (config.dbMode === 'file') {
34+
if (typeof pageContent === 'string') {
35+
try {
36+
return JSON.parse(pageContent)
37+
} catch (e) {
38+
return pageContent
39+
}
40+
}
41+
return pageContent
42+
}
43+
44+
if (pageContent && typeof pageContent === 'object') {
45+
return JSON.stringify(pageContent)
46+
}
47+
48+
return pageContent
49+
}
50+
2751
export default class PageService {
2852
constructor() {
2953
this.store = createStore('pages', {
30-
indexes: [{ fieldName: 'route', unique: true }]
54+
indexes: [{ fieldName: 'route', unique: true }],
55+
namingFields: ['name']
3156
})
3257

3358
this.userInfo = {
@@ -74,9 +99,7 @@ export default class PageService {
7499
const model = params.isPage ? this.pageModel : this.folderModel
75100
const pageData = { ...model, ...params }
76101

77-
if (pageData.page_content && typeof pageData.page_content === 'object') {
78-
pageData.page_content = JSON.stringify(pageData.page_content)
79-
}
102+
pageData.page_content = formatPageContentForStorage(pageData.page_content)
80103

81104
const result = await this.store.insert(pageData)
82105
const { _id } = result
@@ -87,8 +110,9 @@ export default class PageService {
87110

88111
async update(id, params) {
89112
const updateData = { ...params }
90-
if (updateData.page_content && typeof updateData.page_content === 'object') {
91-
updateData.page_content = JSON.stringify(updateData.page_content)
113+
114+
if (Object.prototype.hasOwnProperty.call(updateData, 'page_content')) {
115+
updateData.page_content = formatPageContentForStorage(updateData.page_content)
92116
}
93117

94118
await this.store.update({ _id: id }, { $set: updateData })

0 commit comments

Comments
 (0)