easyorders 0.1.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.
Files changed (32) hide show
  1. package/README.md +94 -0
  2. package/cli/bin/cli.ts +240 -0
  3. package/cli/server/api.ts +56 -0
  4. package/cli/server/index.ts +320 -0
  5. package/cli/server/token-store.ts +35 -0
  6. package/cli/server/tunnel.ts +161 -0
  7. package/cli/template/theme/config.json +123 -0
  8. package/cli/template/theme/schema.json +145 -0
  9. package/cli/template/theme/script.js +412 -0
  10. package/cli/template/theme/sections/breadcrumbs.liquid +17 -0
  11. package/cli/template/theme/sections/categories.liquid +10 -0
  12. package/cli/template/theme/sections/fake-counter.liquid +27 -0
  13. package/cli/template/theme/sections/fake-stock.liquid +6 -0
  14. package/cli/template/theme/sections/fake-visitor.liquid +6 -0
  15. package/cli/template/theme/sections/featured-products.liquid +110 -0
  16. package/cli/template/theme/sections/fixed-buy-button.liquid +46 -0
  17. package/cli/template/theme/sections/footer.liquid +129 -0
  18. package/cli/template/theme/sections/gallery.liquid +61 -0
  19. package/cli/template/theme/sections/header.liquid +152 -0
  20. package/cli/template/theme/sections/home-products-grid.liquid +110 -0
  21. package/cli/template/theme/sections/list-products.liquid +93 -0
  22. package/cli/template/theme/sections/order-invoice.liquid +154 -0
  23. package/cli/template/theme/sections/product-description.liquid +30 -0
  24. package/cli/template/theme/sections/product-details.liquid +63 -0
  25. package/cli/template/theme/sections/products-grid.liquid +86 -0
  26. package/cli/template/theme/sections/related-products.liquid +88 -0
  27. package/cli/template/theme/sections/reviews.liquid +55 -0
  28. package/cli/template/theme/sections/slider.liquid +43 -0
  29. package/cli/template/theme/sections/thanks.liquid +33 -0
  30. package/cli/template/theme/style.css +3923 -0
  31. package/cli/template/theme/theme-data.json +9 -0
  32. package/package.json +37 -0
@@ -0,0 +1,35 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ export interface StoredToken {
5
+ store_name: string;
6
+ store_id: string;
7
+ token_id: string;
8
+ token: string;
9
+ expires_at: string;
10
+ }
11
+
12
+ const TOKEN_FILE = path.resolve(process.cwd(), ".cli-tokens.json");
13
+
14
+ function loadAll(): StoredToken[] {
15
+ try {
16
+ const raw = fs.readFileSync(TOKEN_FILE, "utf-8");
17
+ const parsed: unknown = JSON.parse(raw);
18
+ if (Array.isArray(parsed)) return parsed as StoredToken[];
19
+ } catch {}
20
+ return [];
21
+ }
22
+
23
+ function saveAll(tokens: StoredToken[]): void {
24
+ fs.writeFileSync(TOKEN_FILE, JSON.stringify(tokens, null, 2), "utf-8");
25
+ }
26
+
27
+ export function getToken(storeName: string): StoredToken | undefined {
28
+ return loadAll().find((t) => t.store_name === storeName);
29
+ }
30
+
31
+ export function saveToken(entry: StoredToken): void {
32
+ const tokens = loadAll().filter((t) => t.store_name !== entry.store_name);
33
+ tokens.push(entry);
34
+ saveAll(tokens);
35
+ }
@@ -0,0 +1,161 @@
1
+ import "dotenv/config";
2
+ import { Tunnel } from "cloudflared";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+
6
+ const CYAN = "\x1b[36m";
7
+ const GREEN = "\x1b[32m";
8
+ const YELLOW = "\x1b[33m";
9
+ const RED = "\x1b[31m";
10
+ const DIM = "\x1b[2m";
11
+ const BOLD = "\x1b[1m";
12
+ const RESET = "\x1b[0m";
13
+ const UNDERLINE = "\x1b[4m";
14
+
15
+ const STORE_DOMAIN =
16
+ process.env.STORE_DOMAIN || "myeasyorders.com";
17
+
18
+ const port = Number(process.env.PORT) || 4000;
19
+ const URL_FILE = path.resolve(process.cwd(), ".tunnel-url");
20
+ const PID_FILE = path.resolve(process.cwd(), ".tunnel-pid");
21
+ const AUTH_READY_FILE = path.resolve(process.cwd(), ".auth-ready");
22
+
23
+ function isAlive(pid: number): boolean {
24
+ try {
25
+ process.kill(pid, 0);
26
+ return true;
27
+ } catch {
28
+ return false;
29
+ }
30
+ }
31
+
32
+ function alreadyRunning(): boolean {
33
+ try {
34
+ const pid = parseInt(fs.readFileSync(PID_FILE, "utf-8").trim(), 10);
35
+ if (isAlive(pid)) {
36
+ const url = fs.readFileSync(URL_FILE, "utf-8").trim();
37
+ console.log(` ${DIM}Tunnel already running (pid ${pid})${RESET}`);
38
+ console.log(
39
+ ` ${DIM}Public URL:${RESET} ${CYAN}${UNDERLINE}${url}${RESET}`
40
+ );
41
+ return true;
42
+ }
43
+ } catch {}
44
+ return false;
45
+ }
46
+
47
+ function spinner(text: string): NodeJS.Timeout {
48
+ const frames = [" ◐", " ◓", " ◑", " ◒"];
49
+ let i = 0;
50
+ return setInterval(() => {
51
+ process.stdout.write(
52
+ `\r${CYAN}${frames[i++ % frames.length]}${RESET} ${DIM}${text}${RESET}`
53
+ );
54
+ }, 120);
55
+ }
56
+
57
+ function cleanup() {
58
+ try {
59
+ fs.unlinkSync(URL_FILE);
60
+ } catch {}
61
+ try {
62
+ fs.unlinkSync(PID_FILE);
63
+ } catch {}
64
+ }
65
+
66
+ interface AuthData {
67
+ store_name: string;
68
+ code: string;
69
+ }
70
+
71
+ function waitForAuthReady(): Promise<AuthData> {
72
+ return new Promise((resolve) => {
73
+ const tryRead = () => {
74
+ if (fs.existsSync(AUTH_READY_FILE)) {
75
+ const raw = fs.readFileSync(AUTH_READY_FILE, "utf-8").trim();
76
+ try {
77
+ const data = JSON.parse(raw) as AuthData;
78
+ return resolve(data);
79
+ } catch {
80
+ return resolve({ store_name: "your-store", code: "" });
81
+ }
82
+ }
83
+ };
84
+ tryRead();
85
+ const check = setInterval(() => {
86
+ if (fs.existsSync(AUTH_READY_FILE)) {
87
+ clearInterval(check);
88
+ tryRead();
89
+ }
90
+ }, 500);
91
+ });
92
+ }
93
+
94
+ async function main() {
95
+ if (alreadyRunning()) return;
96
+
97
+ const authData = await waitForAuthReady();
98
+
99
+ fs.writeFileSync(PID_FILE, String(process.pid), "utf-8");
100
+
101
+ const spin = spinner("Starting Cloudflare tunnel...");
102
+ const tunnel = Tunnel.quick(`http://127.0.0.1:${port}`);
103
+
104
+ const tunnelUrl = await new Promise<string>((resolve) => {
105
+ tunnel.once("url", (url) => resolve(url));
106
+ });
107
+
108
+ clearInterval(spin);
109
+ process.stdout.write("\r\x1b[K");
110
+
111
+ fs.writeFileSync(URL_FILE, tunnelUrl, "utf-8");
112
+
113
+ const themeUrl = `${tunnelUrl}/theme`;
114
+ const storeUrl = `https://${authData.store_name}.${STORE_DOMAIN}?load_theme=${encodeURIComponent(themeUrl)}&code=${authData.code}`;
115
+
116
+ console.log("");
117
+ console.log(`${GREEN}${BOLD} ✔ Tunnel is live${RESET}`);
118
+ console.log("");
119
+ console.log(
120
+ ` ${DIM}Public URL:${RESET} ${CYAN}${UNDERLINE}${tunnelUrl}${RESET}`
121
+ );
122
+ console.log(
123
+ ` ${GREEN}${BOLD}✔${RESET} ${DIM}Open your store:${RESET} ${CYAN}${UNDERLINE}${storeUrl}${RESET}`
124
+ );
125
+
126
+ console.log(` ${DIM}Local:${RESET} http://127.0.0.1:${port}`);
127
+ console.log(` ${DIM}─────────────────────────────────────────${RESET}`);
128
+ console.log("");
129
+
130
+ tunnel.once("connected", (conn) => {
131
+ console.log(
132
+ ` ${GREEN}●${RESET} ${DIM}Connected via${RESET} ${BOLD}${
133
+ conn.location
134
+ }${RESET} ${DIM}(${conn.id.slice(0, 8)})${RESET}`
135
+ );
136
+ });
137
+
138
+ tunnel.on("exit", (code, signal) => {
139
+ if (code !== 0) {
140
+ console.log(
141
+ `\n ${RED}✖ Tunnel exited${RESET} ${DIM}(code ${code}, signal ${signal})${RESET}\n`
142
+ );
143
+ }
144
+ cleanup();
145
+ });
146
+
147
+ tunnel.on("error", (err) => {
148
+ console.log(` ${YELLOW}⚠ ${err.message}${RESET}`);
149
+ });
150
+
151
+ process.once("SIGINT", () => {
152
+ cleanup();
153
+ tunnel.stop();
154
+ });
155
+ process.once("SIGTERM", () => {
156
+ cleanup();
157
+ tunnel.stop();
158
+ });
159
+ }
160
+
161
+ main();
@@ -0,0 +1,123 @@
1
+ {
2
+ "footer": {
3
+ "logo": "",
4
+ "pages": [
5
+ {
6
+ "slug": "terms-and-conditions",
7
+ "title": "شروط الاستخدام"
8
+ },
9
+ {
10
+ "slug": "shipping-policy",
11
+ "title": "سياسة الشحن"
12
+ },
13
+ {
14
+ "slug": "refund-policy",
15
+ "title": "سياسة الاستبدال و الاسترجاع"
16
+ },
17
+ {
18
+ "slug": "privacy-policy",
19
+ "title": "سياسات الخصوصية"
20
+ }
21
+ ],
22
+ "social": [
23
+ {
24
+ "url": "https://facebook.com",
25
+ "type": "facebook"
26
+ },
27
+ {
28
+ "url": "https://facebook.com",
29
+ "type": "instagram"
30
+ },
31
+ {
32
+ "url": "https://facebook.com",
33
+ "type": "twitter"
34
+ },
35
+ {
36
+ "url": "https://facebook.com",
37
+ "type": "linkedin"
38
+ },
39
+ {
40
+ "url": "https://facebook.com",
41
+ "type": "tiktok"
42
+ },
43
+ {
44
+ "url": "https://facebook.com",
45
+ "type": "youtube"
46
+ }
47
+ ],
48
+ "categories": [
49
+ {
50
+ "name": "Men",
51
+ "slug": "womens-accessories"
52
+ },
53
+ {
54
+ "name": "Women",
55
+ "slug": "mens-accessories"
56
+ },
57
+ {
58
+ "name": "New Arrival",
59
+ "slug": "98"
60
+ }
61
+ ],
62
+ "payment_img": "https://easyorders.fra1.digitaloceanspaces.com/1773958283329740254payment_icons.svg",
63
+ "is_use_config": true
64
+ },
65
+ "header": {
66
+ "logo": "",
67
+ "links": [
68
+ {
69
+ "url": "/collections/womens-accessories",
70
+ "name": "Men",
71
+ "slug": "womens-accessories",
72
+ "type": "category"
73
+ },
74
+ {
75
+ "url": "/collections/mens-accessories",
76
+ "name": "Women",
77
+ "slug": "mens-accessories",
78
+ "type": "category"
79
+ },
80
+ {
81
+ "url": "/collections/98",
82
+ "name": "New Arrival",
83
+ "slug": "98",
84
+ "type": "category"
85
+ },
86
+ {
87
+ "url": "/collections/30-off-tree-runner-go-tree-gliders",
88
+ "name": "Sale",
89
+ "slug": "30-off-tree-runner-go-tree-gliders",
90
+ "type": "category"
91
+ }
92
+ ],
93
+ "is_use_config": true
94
+ },
95
+ "palette": {
96
+ "ft_bg": "",
97
+ "hd_bg": "#a4e6b6",
98
+ "ann_bg": "#000000",
99
+ "ft_text": "",
100
+ "hd_text": "",
101
+ "ann_text": "",
102
+ "buy_btn_bg": "",
103
+ "crt_btn_bg": "",
104
+ "buy_btn_text": "",
105
+ "crt_btn_text": "",
106
+ "buy_btn_border": "",
107
+ "crt_btn_border": ""
108
+ },
109
+ "theme_data": {
110
+ "hero_slides": [],
111
+ "color_scheme": "light",
112
+ "color_primary": "#1A1A2E",
113
+ "hero_headline": "Welcome to Our Store",
114
+ "font_size_base": 16,
115
+ "hero_show_overlay": true,
116
+ "product_badge_types": ["sale", "new"]
117
+ },
118
+ "announcement_bar": {
119
+ "text": ["it works", "Free shipping for 1000 EGP", "Genuine Quality"],
120
+ "type": "marquee",
121
+ "is_use_config": true
122
+ }
123
+ }
@@ -0,0 +1,145 @@
1
+ [
2
+ {
3
+ "name": "hero_headline",
4
+ "type": "string",
5
+ "default": "Welcome to Our Store",
6
+ "description": "Hero headline text"
7
+ },
8
+ {
9
+ "name": "font_size_base",
10
+ "type": "number",
11
+ "default": 16,
12
+ "description": "Base font size in px"
13
+ },
14
+ {
15
+ "name": "color_primary",
16
+ "type": "color",
17
+ "default": "#1A1A2E",
18
+ "description": "Primary brand color"
19
+ },
20
+ {
21
+ "name": "hero_show_overlay",
22
+ "type": "boolean",
23
+ "default": true,
24
+ "description": "Show dark overlay on hero image"
25
+ },
26
+ {
27
+ "name": "color_scheme",
28
+ "type": "select",
29
+ "default": "light",
30
+ "options": [
31
+ {
32
+ "label": "Light",
33
+ "value": "light"
34
+ },
35
+ {
36
+ "label": "Dark",
37
+ "value": "dark"
38
+ },
39
+ {
40
+ "label": "System default",
41
+ "value": "system"
42
+ }
43
+ ],
44
+ "description": "Base color scheme"
45
+ },
46
+ {
47
+ "name": "product_badge_types",
48
+ "type": "multi_select",
49
+ "default": ["sale", "new"],
50
+ "options": [
51
+ {
52
+ "label": "Sale",
53
+ "value": "sale"
54
+ },
55
+ {
56
+ "label": "New arrival",
57
+ "value": "new"
58
+ },
59
+ {
60
+ "label": "Bestseller",
61
+ "value": "bestseller"
62
+ },
63
+ {
64
+ "label": "Low stock",
65
+ "value": "low_stock"
66
+ },
67
+ {
68
+ "label": "Sold out",
69
+ "value": "sold_out"
70
+ }
71
+ ],
72
+ "description": "Active product badge types"
73
+ },
74
+ {
75
+ "name": "hero_slides",
76
+ "type": "object_array",
77
+ "fields": [
78
+ {
79
+ "name": "title",
80
+ "type": "string",
81
+ "default": "",
82
+ "description": "Slide headline"
83
+ },
84
+ {
85
+ "name": "overlay_opacity",
86
+ "type": "number",
87
+ "default": 40,
88
+ "description": "Overlay darkness (0–100)"
89
+ },
90
+ {
91
+ "name": "text_color",
92
+ "type": "color",
93
+ "default": "#FFFFFF",
94
+ "description": "Slide text color"
95
+ },
96
+ {
97
+ "name": "enabled",
98
+ "type": "boolean",
99
+ "default": true,
100
+ "description": "Enable this slide"
101
+ },
102
+ {
103
+ "name": "cta_style",
104
+ "type": "select",
105
+ "default": "filled",
106
+ "options": [
107
+ {
108
+ "label": "Filled",
109
+ "value": "filled"
110
+ },
111
+ {
112
+ "label": "Outline",
113
+ "value": "outline"
114
+ },
115
+ {
116
+ "label": "Text only",
117
+ "value": "text"
118
+ }
119
+ ],
120
+ "description": "CTA button style"
121
+ },
122
+ {
123
+ "name": "show_on_devices",
124
+ "type": "multi_select",
125
+ "default": ["desktop", "mobile"],
126
+ "options": [
127
+ {
128
+ "label": "Desktop",
129
+ "value": "desktop"
130
+ },
131
+ {
132
+ "label": "Tablet",
133
+ "value": "tablet"
134
+ },
135
+ {
136
+ "label": "Mobile",
137
+ "value": "mobile"
138
+ }
139
+ ],
140
+ "description": "Show this slide on"
141
+ }
142
+ ],
143
+ "description": "Hero slide"
144
+ }
145
+ ]