mango-cms 0.1.1 → 0.1.3
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/cli.js +72 -29
- package/default/config/config/users.js +1 -1
- package/default/package.json +8 -0
- package/package.json +1 -1
- package/default/config/plugins/mango-stand/index.js +0 -206
package/cli.js
CHANGED
|
@@ -10,6 +10,64 @@ const AdmZip = require('adm-zip');
|
|
|
10
10
|
|
|
11
11
|
const program = new Command();
|
|
12
12
|
|
|
13
|
+
// Helper function to download and extract src.zip
|
|
14
|
+
async function ensureSrcExists(mangoCmsRoot) {
|
|
15
|
+
const srcDir = path.join(mangoCmsRoot, 'src');
|
|
16
|
+
if (!fs.existsSync(srcDir)) {
|
|
17
|
+
console.log('Downloading Mango CMS source files...');
|
|
18
|
+
const response = await axios({
|
|
19
|
+
method: 'get',
|
|
20
|
+
url: 'https://mango-cms.s3.amazonaws.com/src.zip',
|
|
21
|
+
responseType: 'arraybuffer'
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const tempZipPath = path.join(mangoCmsRoot, 'src.zip');
|
|
25
|
+
fs.writeFileSync(tempZipPath, response.data);
|
|
26
|
+
|
|
27
|
+
console.log('Extracting source files...');
|
|
28
|
+
const zip = new AdmZip(tempZipPath);
|
|
29
|
+
zip.extractAllTo(mangoCmsRoot, true);
|
|
30
|
+
|
|
31
|
+
// Clean up the zip file
|
|
32
|
+
fs.unlinkSync(tempZipPath);
|
|
33
|
+
console.log('Source files installed successfully.');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Helper function to validate and prompt for license if needed
|
|
38
|
+
async function ensureLicenseExists(configPath) {
|
|
39
|
+
let settings = { license: null };
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
if (fs.existsSync(configPath)) {
|
|
43
|
+
settings = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
44
|
+
}
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.log('Error reading settings file:', error.message);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!settings.license) {
|
|
50
|
+
const answer = await inquirer.prompt([
|
|
51
|
+
{
|
|
52
|
+
type: 'input',
|
|
53
|
+
name: 'license',
|
|
54
|
+
message: 'Enter your license key:',
|
|
55
|
+
validate: input => {
|
|
56
|
+
if (!input) return 'License key is required';
|
|
57
|
+
if (input !== 'admin') return 'Invalid license key';
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
settings.license = answer.license;
|
|
64
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
65
|
+
fs.writeFileSync(configPath, JSON.stringify(settings, null, 2));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return settings.license;
|
|
69
|
+
}
|
|
70
|
+
|
|
13
71
|
program
|
|
14
72
|
.name('mango')
|
|
15
73
|
.description('Mango CMS CLI');
|
|
@@ -44,28 +102,7 @@ program
|
|
|
44
102
|
]);
|
|
45
103
|
|
|
46
104
|
// First, handle the src.zip download if needed
|
|
47
|
-
|
|
48
|
-
const srcDir = path.join(mangoCmsRoot, 'src');
|
|
49
|
-
|
|
50
|
-
if (!fs.existsSync(srcDir)) {
|
|
51
|
-
console.log('Downloading Mango CMS source files...');
|
|
52
|
-
const response = await axios({
|
|
53
|
-
method: 'get',
|
|
54
|
-
url: 'https://mango-cms.s3.amazonaws.com/src.zip',
|
|
55
|
-
responseType: 'arraybuffer'
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
const tempZipPath = path.join(mangoCmsRoot, 'src.zip');
|
|
59
|
-
fs.writeFileSync(tempZipPath, response.data);
|
|
60
|
-
|
|
61
|
-
console.log('Extracting source files...');
|
|
62
|
-
const zip = new AdmZip(tempZipPath);
|
|
63
|
-
zip.extractAllTo(mangoCmsRoot, true);
|
|
64
|
-
|
|
65
|
-
// Clean up the zip file
|
|
66
|
-
fs.unlinkSync(tempZipPath);
|
|
67
|
-
console.log('Source files installed successfully.');
|
|
68
|
-
}
|
|
105
|
+
await ensureSrcExists(__dirname);
|
|
69
106
|
|
|
70
107
|
// Now handle the project creation
|
|
71
108
|
const projectDir = path.join(process.cwd(), answers.projectName);
|
|
@@ -80,7 +117,7 @@ program
|
|
|
80
117
|
const configDir = path.join(projectDir, 'config');
|
|
81
118
|
|
|
82
119
|
// Create or update settings.json
|
|
83
|
-
const settingsPath = path.join(configDir, 'settings.json');
|
|
120
|
+
const settingsPath = path.join(configDir, 'config/settings.json');
|
|
84
121
|
const settings = {
|
|
85
122
|
license: answers.license
|
|
86
123
|
};
|
|
@@ -90,7 +127,8 @@ program
|
|
|
90
127
|
✨ Project ${answers.projectName} created successfully!
|
|
91
128
|
To get started:
|
|
92
129
|
cd ${answers.projectName}
|
|
93
|
-
|
|
130
|
+
npm install
|
|
131
|
+
npm mango start
|
|
94
132
|
`);
|
|
95
133
|
} catch (error) {
|
|
96
134
|
console.error('Error creating project:', error.message);
|
|
@@ -101,13 +139,20 @@ To get started:
|
|
|
101
139
|
program
|
|
102
140
|
.command('start')
|
|
103
141
|
.description('Start the Mango CMS in watch mode')
|
|
104
|
-
.action(() => {
|
|
142
|
+
.action(async () => {
|
|
105
143
|
try {
|
|
144
|
+
|
|
145
|
+
// Check for license in settings.json
|
|
146
|
+
const settingsPath = path.join(process.cwd(), 'config/config/settings.json');
|
|
147
|
+
await ensureLicenseExists(settingsPath);
|
|
148
|
+
|
|
149
|
+
// Ensure src exists
|
|
150
|
+
await ensureSrcExists(__dirname);
|
|
151
|
+
|
|
106
152
|
// Path to @mango-cms/core inside node_modules
|
|
107
153
|
const cmsPackagePath = path.resolve(__dirname);
|
|
108
154
|
// User's project root (where they run "mango start")
|
|
109
|
-
|
|
110
|
-
const userProjectRoot = '/Users/coltonneifert/Sites/test-2'
|
|
155
|
+
const userProjectRoot = process.cwd();
|
|
111
156
|
|
|
112
157
|
console.log(`Starting Mango CMS...`);
|
|
113
158
|
console.log(`CMS package located at: ${cmsPackagePath}`);
|
|
@@ -122,8 +167,6 @@ program
|
|
|
122
167
|
stdio: 'inherit',
|
|
123
168
|
env: {
|
|
124
169
|
...process.env,
|
|
125
|
-
// NODE_OPTIONS: '--trace-warnings',
|
|
126
|
-
// PROJECT_CONFIG: configDir,
|
|
127
170
|
NODE_PATH: path.resolve(cmsPackagePath, 'node_modules'),
|
|
128
171
|
MANGO_ROOT: cmsPackagePath,
|
|
129
172
|
PROJECT_ROOT: userProjectRoot
|
package/package.json
CHANGED
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
// import CMS from '../../../mango/src/cms' (for use like {address: CMS.fields.Address()})
|
|
2
|
-
import fields from '@cms/1. build/fields'
|
|
3
|
-
import { readEntries, readEntry } from '../../../mango/src/cms/1. build/libraries/mongo'
|
|
4
|
-
|
|
5
|
-
const billing = {
|
|
6
|
-
fields: {
|
|
7
|
-
paymentId: String,
|
|
8
|
-
firstName: { type: String, search: { enabled: true, weight: 5 } },
|
|
9
|
-
lastName: { type: String, search: { enabled: true, weight: 5 } },
|
|
10
|
-
phoneNumber: String,
|
|
11
|
-
address: fields.Address(),
|
|
12
|
-
payment: {
|
|
13
|
-
fields: {
|
|
14
|
-
creditCard: {
|
|
15
|
-
fields: {
|
|
16
|
-
cardNumber: String,
|
|
17
|
-
cardType: String,
|
|
18
|
-
expirationDate: String,
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
bankAccount: {
|
|
22
|
-
fields: {
|
|
23
|
-
accountNumber: String,
|
|
24
|
-
accountType: String,
|
|
25
|
-
echeckType: String,
|
|
26
|
-
nameOnAccount: String,
|
|
27
|
-
routingNumber: String
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const Items = {
|
|
36
|
-
name: 'items',
|
|
37
|
-
singular: 'item',
|
|
38
|
-
fields: {
|
|
39
|
-
price: { type: 'Int' },
|
|
40
|
-
stock: { type: 'Int' },
|
|
41
|
-
description: fields.PlainText(),
|
|
42
|
-
},
|
|
43
|
-
hooks: {
|
|
44
|
-
sold({ item }) {
|
|
45
|
-
console.log('sold', item.title)
|
|
46
|
-
},
|
|
47
|
-
addedToCart({ item }) {
|
|
48
|
-
console.log('addedToCart', item.title)
|
|
49
|
-
},
|
|
50
|
-
removedFromCart({ item }) {
|
|
51
|
-
console.log('removedFromCart', item.title)
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const Discounts = {
|
|
57
|
-
name: 'discounts',
|
|
58
|
-
singular: 'discount',
|
|
59
|
-
permissions: {
|
|
60
|
-
public: ['create', 'read']
|
|
61
|
-
},
|
|
62
|
-
fields: {
|
|
63
|
-
type: fields.Select({ options: ['coupon', 'sale'], required: true }),
|
|
64
|
-
code: { required: true, translateInput: i => i.toLowerCase() },
|
|
65
|
-
limitations: {
|
|
66
|
-
fields: {
|
|
67
|
-
items: fields.Relationship({ collection: 'item' }),
|
|
68
|
-
perCustomer: 'Int',
|
|
69
|
-
totalUses: 'Int',
|
|
70
|
-
perOrder: 'Int',
|
|
71
|
-
quantity: 'Int',
|
|
72
|
-
// remainingUses: {
|
|
73
|
-
// computed: doc => doc.limitations.totalUses ? /* Get times used and subtract from total uses */ : null
|
|
74
|
-
// }
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
freeShipping: Boolean,
|
|
78
|
-
discount: {
|
|
79
|
-
inputExample: '$12.50 or 50% etc.',
|
|
80
|
-
inputType: String,
|
|
81
|
-
validate: input => ({ valid: (!!input.match(/\$[0-9]+/) || input.match(/[0-9]\%+/)) ? true : false, response: 'Should be formatted like `100%` or `$12.50`' }),
|
|
82
|
-
translateInput: input => ({
|
|
83
|
-
type: !!input.match(/\$[0-9]+/) ? 'fixed' : 'percentage',
|
|
84
|
-
value: !!input.match(/\$[0-9]+/) ? input.match(/\$\d+(?:\.\d+)?/)[0].replace('$', '') : input.match(/(\d+|\d+[.,]\d{1,2})(?=\s*%)/)[0].replace('%', '')
|
|
85
|
-
}),
|
|
86
|
-
fields: {
|
|
87
|
-
type: String,
|
|
88
|
-
value: 'Float'
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const Carts = {
|
|
96
|
-
name: 'carts',
|
|
97
|
-
singular: 'cart',
|
|
98
|
-
permissions: {
|
|
99
|
-
public: ['create', 'read', 'update']
|
|
100
|
-
},
|
|
101
|
-
fields: {
|
|
102
|
-
|
|
103
|
-
title: { required: false },
|
|
104
|
-
|
|
105
|
-
transactionId: String,
|
|
106
|
-
finalized: Boolean,
|
|
107
|
-
completed: Boolean,
|
|
108
|
-
completedDate: fields.Timestamp(),
|
|
109
|
-
cartItems: [{
|
|
110
|
-
fields: {
|
|
111
|
-
quantity: 'Int',
|
|
112
|
-
discount: 'Float',
|
|
113
|
-
item: fields.Relationship({ collection: 'item', single: true }),
|
|
114
|
-
}
|
|
115
|
-
}],
|
|
116
|
-
coupons: fields.Relationship({ collection: 'discount' }),
|
|
117
|
-
|
|
118
|
-
customerId: String,
|
|
119
|
-
billing,
|
|
120
|
-
shipping: {
|
|
121
|
-
fields: {
|
|
122
|
-
method: String,
|
|
123
|
-
price: String,
|
|
124
|
-
addressId: String,
|
|
125
|
-
firstName: String,
|
|
126
|
-
lastName: String,
|
|
127
|
-
phoneNumber: String,
|
|
128
|
-
address: fields.Address(),
|
|
129
|
-
free: { type: Boolean, computed: doc => doc?.coupons?.find(c => c.freeShipping) }
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
|
|
133
|
-
price: {
|
|
134
|
-
fields: {
|
|
135
|
-
subtotal: 'Float',
|
|
136
|
-
discount: 'Float',
|
|
137
|
-
total: 'Float',
|
|
138
|
-
},
|
|
139
|
-
computed: async doc => {
|
|
140
|
-
|
|
141
|
-
let subtotal = Number(doc.cartItems?.reduce((a, i) => a + ((i.item?.price || 0) * i.quantity), 0)) || 0
|
|
142
|
-
let shipping = doc?.shipping?.price || 0
|
|
143
|
-
|
|
144
|
-
let discount = 0
|
|
145
|
-
|
|
146
|
-
if (doc?.coupons?.length) {
|
|
147
|
-
let coupon = doc?.coupons?.[0]
|
|
148
|
-
if (coupon?.limitations?.items) {
|
|
149
|
-
for (let item of doc.cartItems) {
|
|
150
|
-
if (coupon?.limitations?.items?.find(i => i.id == item.item?.id)) {
|
|
151
|
-
if (coupon.discount.type == 'fixed') {
|
|
152
|
-
discount += (item.quantity * coupon.discount.value)
|
|
153
|
-
} else {
|
|
154
|
-
discount += ((item.quantity * item.item.price) * (Number(coupon.discount.value) / 100))
|
|
155
|
-
console.log('discount', discount)
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
} else {
|
|
160
|
-
if (coupon.discount.type == 'fixed') {
|
|
161
|
-
discount = Number(coupon.discount.value > subtotal ? subtotal : coupon.discount.value)
|
|
162
|
-
} else {
|
|
163
|
-
discount = Number(subtotal * (Number(coupon.discount.value) / 100))
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
let activeSales = await readEntries({ collection: 'discounts', search: { type: 'sale', startDate: { $lt: new Date() }, endDate: { $gt: new Date() } } })
|
|
169
|
-
|
|
170
|
-
if (activeSales?.length) {
|
|
171
|
-
for (let item of doc.cartItems) {
|
|
172
|
-
for (let sale of activeSales.sort((a, b) => b.discount.value < a.discount.value ? 1 : -1)) {
|
|
173
|
-
let saleApplies = sale?.limitations?.items?.some(i => i == item.item?.id) || !sale?.limitations?.items?.length
|
|
174
|
-
if (saleApplies) {
|
|
175
|
-
if (sale.discount.type == 'fixed') {
|
|
176
|
-
item.discount = (item.quantity * sale.discount.value)
|
|
177
|
-
} else {
|
|
178
|
-
item.discount = Number((item.quantity * item.item.price) * (Number(sale.discount.value) / 100))
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
discount = Number(doc.cartItems?.reduce((a, i) => a + i.discount, 0))
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
let total = (subtotal + shipping - discount) || 0
|
|
187
|
-
|
|
188
|
-
return {
|
|
189
|
-
subtotal,
|
|
190
|
-
discount,
|
|
191
|
-
total,
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
},
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
export default {
|
|
200
|
-
name: 'mangoStand',
|
|
201
|
-
collections: [
|
|
202
|
-
Items,
|
|
203
|
-
Carts,
|
|
204
|
-
Discounts,
|
|
205
|
-
],
|
|
206
|
-
}
|