Skip to content

Commit a359602

Browse files
authored
Merge pull request #28 from Acode-Foundation/fix-issues
Refactor code structure for improved readability and maintainability
2 parents 35edc00 + 4dc8556 commit a359602

22 files changed

Lines changed: 4894 additions & 3686 deletions

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
node_modules
22
test.mjs
3-
dist.zip
3+
dist.zip
4+
dist/

.vscode/getNet.js

Lines changed: 0 additions & 30 deletions
This file was deleted.

.vscode/server.crt

Lines changed: 0 additions & 16 deletions
This file was deleted.

.vscode/server.key

Lines changed: 0 additions & 18 deletions
This file was deleted.

.vscode/settings.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,9 @@
1616
"settitle",
1717
"warningreport",
1818
"whitespaces"
19-
]
19+
],
20+
"editor.codeActionsOnSave": {
21+
"quickFix.biome": "explicit",
22+
"source.organizeImports.biome": "explicit"
23+
}
2024
}

.vscode/start-dev.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
/* eslint-disable no-console */
2-
const { fork, spawn } = require('child_process');
3-
const path = require('path');
1+
const { fork } = require('node:child_process');
2+
const path = require('node:path');
43

54
main();
65

.vscode/start-server.js

Lines changed: 166 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,183 @@
1-
/* eslint-disable import/no-extraneous-dependencies */
2-
const fs = require('fs');
3-
const path = require('path');
4-
const liveServer = require('live-server');
5-
const getNet = require('./getNet');
1+
const fs = require('node:fs');
2+
const path = require('node:path');
3+
const http = require('node:http');
4+
const ngrok = require('@ngrok/ngrok');
5+
const os = require('node:os');
66

7-
const serverCrt = path.resolve(__dirname, 'server.crt');
8-
const serverKey = path.resolve(__dirname, 'server.key');
7+
const PORT = 5500;
8+
const PROJECT_ROOT = path.resolve(__dirname, '..');
9+
const NGROK_CONFIG_PATHS = [
10+
path.join(os.homedir(), '.config', 'ngrok', 'ngrok.yml'),
11+
path.join(os.homedir(), 'Library', 'Application Support', 'ngrok', 'ngrok.yml'),
12+
path.join(os.homedir(), '.ngrok2', 'ngrok.yml'),
13+
];
914

1015
main();
1116

1217
async function main() {
13-
const { ip: host, port } = await getNet('dev');
14-
process.cwd = () => __dirname;
15-
liveServer.start({
16-
open: false,
17-
port,
18-
host,
19-
cors: true,
20-
root: '../',
21-
ignore: 'node_modules,platforms,plugins',
22-
file: 'index.html',
23-
https: {
24-
cert: fs.readFileSync(serverCrt),
25-
key: fs.readFileSync(serverKey),
26-
passphrase: '1234',
27-
},
28-
middleware: [(req, res, next) => {
29-
const url = req.originalUrl;
30-
const www = '../platforms/android/app/src/main/assets/www/';
31-
32-
if (url === '/cordova.js') {
33-
const file = path.resolve(__dirname, www, 'cordova.js');
34-
sendFile(res, file);
35-
return;
36-
}
18+
try {
19+
// Get ngrok authtoken
20+
const authtoken = await getNgrokAuthtoken();
21+
22+
// Create HTTP server
23+
const server = http.createServer((req, res) => {
24+
handleRequest(req, res);
25+
});
26+
27+
// Start local server
28+
server.listen(PORT, () => {
29+
console.log(`\n✓ Local server running on port ${PORT}`);
30+
});
31+
32+
// Create ngrok tunnel
33+
console.log('✓ Starting ngrok tunnel...');
34+
35+
// Try to use a static domain if available (check ngrok config)
36+
const domain = getStaticDomain();
37+
const connectOptions = {
38+
addr: PORT,
39+
authtoken: authtoken
40+
};
41+
42+
if (domain) {
43+
connectOptions.domain = domain;
44+
console.log(` Using static domain: ${domain}`);
45+
}
46+
47+
const listener = await ngrok.connect(connectOptions);
3748

38-
if (url === '/cordova_plugins.js') {
39-
const file = path.resolve(__dirname, www, 'cordova_plugins.js');
40-
sendFile(res, file);
41-
return;
49+
const url = listener.url();
50+
console.log('\n' + '='.repeat(60));
51+
console.log('🚀 Plugin available at:');
52+
console.log(` ${url}/dist.zip`);
53+
console.log('='.repeat(60) + '\n');
54+
55+
if (!domain) {
56+
console.log('💡 TIP: URL changes each time on free plan.');
57+
console.log(' To get a permanent URL, claim your free static domain:');
58+
console.log(' 1. Visit: https://dashboard.ngrok.com/domains');
59+
console.log(' 2. Click "Create Domain" or "New Domain"');
60+
console.log(' 3. Save the domain (e.g., acode-prettier.ngrok-free.app)');
61+
console.log(' 4. Add to ngrok config: ngrok config edit');
62+
console.log(' 5. Add under agent section:');
63+
console.log(' domain: your-domain.ngrok-free.app\n');
64+
}
65+
66+
console.log('Copy the URL above and paste it in Acode:');
67+
console.log('Settings → Plugins → + → REMOTE\n');
68+
69+
if (process.send) {
70+
process.send('OK');
71+
}
72+
} catch (error) {
73+
console.error('\n❌ Error starting server:');
74+
console.error(error.message);
75+
process.exit(1);
76+
}
77+
}
78+
79+
async function getNgrokAuthtoken() {
80+
// Try to read authtoken from ngrok config file
81+
for (const configPath of NGROK_CONFIG_PATHS) {
82+
if (fs.existsSync(configPath)) {
83+
const config = fs.readFileSync(configPath, 'utf8');
84+
const match = config.match(/authtoken:\s*([^\s]+)/);
85+
if (match) {
86+
return match[1];
4287
}
88+
}
89+
}
4390

44-
next();
45-
}],
46-
});
91+
// If not found, show error
92+
console.error('\n❌ ngrok authtoken not configured!\n');
93+
console.error('Please follow these steps:');
94+
console.error('1. Sign up at https://dashboard.ngrok.com/signup');
95+
console.error('2. Get your authtoken from https://dashboard.ngrok.com/get-started/your-authtoken');
96+
console.error('3. Run: ngrok config add-authtoken <YOUR_TOKEN>\n');
97+
process.exit(1);
98+
}
99+
100+
function getStaticDomain() {
101+
// Try to read static domain from ngrok config file
102+
for (const configPath of NGROK_CONFIG_PATHS) {
103+
if (fs.existsSync(configPath)) {
104+
const config = fs.readFileSync(configPath, 'utf8');
105+
// Look for domain under agent section
106+
const match = config.match(/domain:\s*([^\s]+)/);
107+
if (match) {
108+
return match[1];
109+
}
110+
}
111+
}
47112

48-
process.send('OK');
113+
return null;
49114
}
50115

51-
function sendFile(res, filePath) {
52-
if (fs.existsSync(filePath)) {
53-
const stat = fs.statSync(filePath);
116+
function handleRequest(req, res) {
117+
// Parse URL and remove query string
118+
const url = req.url.split('?')[0];
54119

55-
res.writeHead(200, {
56-
'Content-Type': 'application/javascript',
57-
'Content-Length': stat.size,
58-
});
120+
// Determine file path
121+
let filePath;
122+
if (url === '/' || url === '/index.html') {
123+
filePath = path.join(PROJECT_ROOT, 'index.html');
124+
} else {
125+
filePath = path.join(PROJECT_ROOT, url);
126+
}
127+
128+
// Security check: ensure file is within project root
129+
const normalizedPath = path.normalize(filePath);
130+
if (!normalizedPath.startsWith(PROJECT_ROOT)) {
131+
res.writeHead(403, { 'Content-Type': 'text/plain' });
132+
res.end('403 Forbidden');
133+
return;
134+
}
135+
136+
// Check if file exists
137+
if (!fs.existsSync(filePath)) {
138+
res.writeHead(404, { 'Content-Type': 'text/plain' });
139+
res.end('404 Not Found');
140+
return;
141+
}
59142

60-
const readStream = fs.createReadStream(filePath);
61-
readStream.pipe(res);
143+
// Check if path is a directory
144+
const stat = fs.statSync(filePath);
145+
if (stat.isDirectory()) {
146+
res.writeHead(403, { 'Content-Type': 'text/plain' });
147+
res.end('403 Forbidden: Directory listing not allowed');
62148
return;
63149
}
64150

65-
res.writeHead(404, { 'Content-Type': 'text/plain' });
66-
res.end(`ERROR cannot get ${filePath}`);
151+
// Determine content type
152+
const ext = path.extname(filePath).toLowerCase();
153+
const contentTypes = {
154+
'.html': 'text/html',
155+
'.js': 'application/javascript',
156+
'.json': 'application/json',
157+
'.zip': 'application/zip',
158+
'.css': 'text/css',
159+
'.png': 'image/png',
160+
'.jpg': 'image/jpeg',
161+
'.gif': 'image/gif',
162+
'.svg': 'image/svg+xml',
163+
'.txt': 'text/plain',
164+
};
165+
const contentType = contentTypes[ext] || 'application/octet-stream';
166+
167+
// Serve file
168+
res.writeHead(200, {
169+
'Content-Type': contentType,
170+
'Content-Length': stat.size,
171+
'Access-Control-Allow-Origin': '*',
172+
});
173+
174+
const readStream = fs.createReadStream(filePath);
175+
readStream.on('error', (err) => {
176+
console.error('Error reading file:', err);
177+
if (!res.headersSent) {
178+
res.writeHead(500, { 'Content-Type': 'text/plain' });
179+
}
180+
res.end('500 Internal Server Error');
181+
});
182+
readStream.pipe(res);
67183
}

biome.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"$schema": "https://biomejs.dev/schemas/2.3.8/schema.json",
3+
"vcs": {
4+
"enabled": true,
5+
"clientKind": "git",
6+
"useIgnoreFile": true
7+
},
8+
"files": {
9+
"includes": ["**", "!!**/dist"]
10+
},
11+
"formatter": {
12+
"enabled": true,
13+
"indentStyle": "tab"
14+
},
15+
"linter": {
16+
"enabled": true,
17+
"rules": {
18+
"recommended": true
19+
}
20+
},
21+
"javascript": {
22+
"formatter": {
23+
"quoteStyle": "double"
24+
}
25+
},
26+
"assist": {
27+
"enabled": true,
28+
"actions": {
29+
"source": {
30+
"organizeImports": "on"
31+
}
32+
}
33+
}
34+
}

dist/748.js

Lines changed: 0 additions & 2 deletions
This file was deleted.

dist/748.js.LICENSE.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)