appclean 2.0.0 → 2.0.2
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/.github/workflows/npm-publish.yml +61 -0
- package/DEVELOPMENT.md +84 -0
- package/RELEASE_GUIDE.md +236 -0
- package/dist/index.js +1 -1
- package/dist/ui/client/api/client.d.ts.map +1 -1
- package/dist/ui/client/api/client.js +5 -1
- package/dist/ui/client/api/client.js.map +1 -1
- package/dist/ui/client/app.d.ts +1 -1
- package/dist/ui/client/app.d.ts.map +1 -1
- package/dist/ui/client/app.js +10 -6
- package/dist/ui/client/app.js.map +1 -1
- package/dist/ui/client/pages/appSearch.js +12 -1
- package/dist/ui/client/pages/appSearch.js.map +1 -1
- package/dist/ui/client/pages/dashboard.d.ts.map +1 -1
- package/dist/ui/client/pages/dashboard.js +26 -5
- package/dist/ui/client/pages/dashboard.js.map +1 -1
- package/dist/ui/client/state/appStore.d.ts.map +1 -1
- package/dist/ui/client/state/appStore.js +21 -12
- package/dist/ui/client/state/appStore.js.map +1 -1
- package/dist/ui/client/state/dashboardStore.d.ts.map +1 -1
- package/dist/ui/client/state/dashboardStore.js +9 -3
- package/dist/ui/client/state/dashboardStore.js.map +1 -1
- package/dist/ui/client/styles/animations.css +22 -0
- package/dist/ui/guiServer.d.ts +3 -0
- package/dist/ui/guiServer.d.ts.map +1 -1
- package/dist/ui/guiServer.js +48 -1
- package/dist/ui/guiServer.js.map +1 -1
- package/dist/utils/upgrade.d.ts +2 -1
- package/dist/utils/upgrade.d.ts.map +1 -1
- package/dist/utils/upgrade.js +14 -1
- package/dist/utils/upgrade.js.map +1 -1
- package/package.json +1 -1
- package/scripts/publish-npm.sh +64 -0
- package/src/index.ts +1 -1
- package/src/ui/client/api/client.ts +6 -1
- package/src/ui/client/app.ts +15 -11
- package/src/ui/client/pages/appSearch.ts +14 -1
- package/src/ui/client/pages/dashboard.ts +27 -5
- package/src/ui/client/state/appStore.ts +24 -12
- package/src/ui/client/state/dashboardStore.ts +13 -3
- package/src/ui/client/styles/animations.css +22 -0
- package/src/ui/guiServer.ts +67 -1
- package/src/utils/upgrade.ts +18 -1
|
@@ -57,16 +57,20 @@ export class AppStore extends Store<AppStoreState> {
|
|
|
57
57
|
const response = await fetch('/api/apps/list');
|
|
58
58
|
if (!response.ok) throw new Error('Failed to load apps');
|
|
59
59
|
|
|
60
|
-
const
|
|
60
|
+
const json = await response.json();
|
|
61
|
+
const data = json.data || json; // Handle wrapped { success, data } format
|
|
62
|
+
|
|
61
63
|
this.setState({
|
|
62
|
-
apps: data.apps,
|
|
63
|
-
total: data.total,
|
|
64
|
-
page: data.page,
|
|
64
|
+
apps: data.apps || [],
|
|
65
|
+
total: data.total || 0,
|
|
66
|
+
page: data.page || 1,
|
|
65
67
|
isLoading: false,
|
|
66
68
|
});
|
|
67
69
|
} catch (error) {
|
|
70
|
+
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
71
|
+
console.error('Failed to load apps:', errorMsg);
|
|
68
72
|
this.setState({
|
|
69
|
-
error:
|
|
73
|
+
error: errorMsg,
|
|
70
74
|
isLoading: false,
|
|
71
75
|
});
|
|
72
76
|
}
|
|
@@ -88,15 +92,19 @@ export class AppStore extends Store<AppStoreState> {
|
|
|
88
92
|
const response = await fetch(`/api/apps/search?${params}`);
|
|
89
93
|
if (!response.ok) throw new Error('Failed to search apps');
|
|
90
94
|
|
|
91
|
-
const
|
|
95
|
+
const json = await response.json();
|
|
96
|
+
const data = json.data || json; // Handle wrapped { success, data } format
|
|
97
|
+
|
|
92
98
|
this.setState({
|
|
93
|
-
apps: data.apps,
|
|
94
|
-
total: data.count,
|
|
99
|
+
apps: data.apps || [],
|
|
100
|
+
total: data.count || 0,
|
|
95
101
|
isLoading: false,
|
|
96
102
|
});
|
|
97
103
|
} catch (error) {
|
|
104
|
+
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
105
|
+
console.error('Failed to search apps:', errorMsg);
|
|
98
106
|
this.setState({
|
|
99
|
-
error:
|
|
107
|
+
error: errorMsg,
|
|
100
108
|
isLoading: false,
|
|
101
109
|
});
|
|
102
110
|
}
|
|
@@ -145,15 +153,19 @@ export class AppStore extends Store<AppStoreState> {
|
|
|
145
153
|
const response = await fetch(`/api/apps/search?${params}`);
|
|
146
154
|
if (!response.ok) throw new Error('Failed to load more apps');
|
|
147
155
|
|
|
148
|
-
const
|
|
156
|
+
const json = await response.json();
|
|
157
|
+
const data = json.data || json; // Handle wrapped { success, data } format
|
|
158
|
+
|
|
149
159
|
this.setState({
|
|
150
|
-
apps: [...this.state.apps, ...data.apps],
|
|
160
|
+
apps: [...this.state.apps, ...(data.apps || [])],
|
|
151
161
|
page: nextPage,
|
|
152
162
|
isLoading: false,
|
|
153
163
|
});
|
|
154
164
|
} catch (error) {
|
|
165
|
+
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
166
|
+
console.error('Failed to load next page:', errorMsg);
|
|
155
167
|
this.setState({
|
|
156
|
-
error:
|
|
168
|
+
error: errorMsg,
|
|
157
169
|
isLoading: false,
|
|
158
170
|
});
|
|
159
171
|
}
|
|
@@ -44,17 +44,27 @@ export class DashboardStore extends Store<DashboardStoreState> {
|
|
|
44
44
|
this.setState({ isLoading: true, error: null });
|
|
45
45
|
try {
|
|
46
46
|
const response = await fetch('/api/dashboard/stats');
|
|
47
|
-
if (!response.ok) throw new Error(
|
|
47
|
+
if (!response.ok) throw new Error(`Failed to load dashboard stats: ${response.status}`);
|
|
48
|
+
|
|
49
|
+
const data = await response.json();
|
|
50
|
+
|
|
51
|
+
// Extract stats from API response format { success: true, data: {...} }
|
|
52
|
+
const stats = data.data || data;
|
|
53
|
+
|
|
54
|
+
if (!stats) {
|
|
55
|
+
throw new Error('Invalid response format from server');
|
|
56
|
+
}
|
|
48
57
|
|
|
49
|
-
const stats = await response.json();
|
|
50
58
|
this.setState({
|
|
51
59
|
stats,
|
|
52
60
|
isLoading: false,
|
|
53
61
|
lastUpdated: Date.now(),
|
|
54
62
|
});
|
|
55
63
|
} catch (error) {
|
|
64
|
+
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
65
|
+
console.error('Failed to load dashboard stats:', errorMsg);
|
|
56
66
|
this.setState({
|
|
57
|
-
error:
|
|
67
|
+
error: errorMsg,
|
|
58
68
|
isLoading: false,
|
|
59
69
|
});
|
|
60
70
|
}
|
|
@@ -39,6 +39,28 @@
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
@keyframes slideUp {
|
|
43
|
+
from {
|
|
44
|
+
opacity: 0;
|
|
45
|
+
transform: translateY(20px);
|
|
46
|
+
}
|
|
47
|
+
to {
|
|
48
|
+
opacity: 1;
|
|
49
|
+
transform: translateY(0);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@keyframes slideIn {
|
|
54
|
+
from {
|
|
55
|
+
opacity: 0;
|
|
56
|
+
transform: translateX(-10px);
|
|
57
|
+
}
|
|
58
|
+
to {
|
|
59
|
+
opacity: 1;
|
|
60
|
+
transform: translateX(0);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
42
64
|
@keyframes slideOutDown {
|
|
43
65
|
from {
|
|
44
66
|
opacity: 1;
|
package/src/ui/guiServer.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { readFileSync, existsSync } from 'fs';
|
|
|
8
8
|
import { join } from 'path';
|
|
9
9
|
import { fileURLToPath } from 'url';
|
|
10
10
|
import { dirname } from 'path';
|
|
11
|
+
import { execFile, spawn, type ChildProcess } from 'child_process';
|
|
11
12
|
import { Logger } from '../utils/logger.js';
|
|
12
13
|
import { sendJson, sendError, parseQueryParams } from './server/middleware/errorHandler.js';
|
|
13
14
|
import { handleAppRoutes } from './server/routes/apps.js';
|
|
@@ -22,6 +23,7 @@ export class GUIServer {
|
|
|
22
23
|
private port: number = 3000;
|
|
23
24
|
private server: any;
|
|
24
25
|
private spaHtml: string | null = null;
|
|
26
|
+
private browserProcess: ChildProcess | null = null;
|
|
25
27
|
|
|
26
28
|
constructor(port: number = 3000) {
|
|
27
29
|
this.port = port;
|
|
@@ -41,9 +43,13 @@ export class GUIServer {
|
|
|
41
43
|
});
|
|
42
44
|
|
|
43
45
|
return new Promise((resolve) => {
|
|
44
|
-
this.server.listen(this.port, () => {
|
|
46
|
+
this.server.listen(this.port, async () => {
|
|
45
47
|
Logger.success(`✨ AppClean GUI running at http://localhost:${this.port}`);
|
|
46
48
|
Logger.info('Press Ctrl+C to stop the server');
|
|
49
|
+
|
|
50
|
+
// Open the default browser
|
|
51
|
+
await this.openBrowser();
|
|
52
|
+
|
|
47
53
|
resolve();
|
|
48
54
|
});
|
|
49
55
|
});
|
|
@@ -53,12 +59,72 @@ export class GUIServer {
|
|
|
53
59
|
* Stop GUI server
|
|
54
60
|
*/
|
|
55
61
|
async stop(): Promise<void> {
|
|
62
|
+
// Close the browser
|
|
63
|
+
await this.closeBrowser();
|
|
64
|
+
|
|
56
65
|
if (this.server) {
|
|
57
66
|
this.server.close();
|
|
58
67
|
Logger.info('GUI server stopped');
|
|
59
68
|
}
|
|
60
69
|
}
|
|
61
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Open the default browser
|
|
73
|
+
*/
|
|
74
|
+
private async openBrowser(): Promise<void> {
|
|
75
|
+
const url = `http://localhost:${this.port}`;
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const platform = process.platform;
|
|
79
|
+
|
|
80
|
+
if (platform === 'darwin') {
|
|
81
|
+
// macOS
|
|
82
|
+
this.browserProcess = execFile('open', [url]);
|
|
83
|
+
} else if (platform === 'win32') {
|
|
84
|
+
// Windows
|
|
85
|
+
this.browserProcess = execFile('cmd', ['/c', 'start', url]);
|
|
86
|
+
} else {
|
|
87
|
+
// Linux and other Unix-like systems
|
|
88
|
+
this.browserProcess = execFile('xdg-open', [url]);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
this.browserProcess.on('error', (error) => {
|
|
92
|
+
Logger.warn(`Could not open browser: ${error.message}`);
|
|
93
|
+
});
|
|
94
|
+
} catch (error) {
|
|
95
|
+
Logger.warn(`Failed to open browser: ${(error as Error).message}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Close the browser
|
|
101
|
+
*/
|
|
102
|
+
private async closeBrowser(): Promise<void> {
|
|
103
|
+
if (!this.browserProcess) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
const platform = process.platform;
|
|
109
|
+
|
|
110
|
+
if (platform === 'win32') {
|
|
111
|
+
// Windows: kill the process
|
|
112
|
+
if (this.browserProcess.pid) {
|
|
113
|
+
process.kill(this.browserProcess.pid, 'SIGTERM');
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
// macOS and Linux: kill the process
|
|
117
|
+
if (this.browserProcess.pid) {
|
|
118
|
+
process.kill(this.browserProcess.pid);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
this.browserProcess = null;
|
|
123
|
+
} catch (error) {
|
|
124
|
+
Logger.debug(`Note: Could not close browser process: ${(error as Error).message}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
62
128
|
/**
|
|
63
129
|
* Main request handler
|
|
64
130
|
*/
|
package/src/utils/upgrade.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { execSync } from 'child_process';
|
|
2
2
|
import { Logger } from './logger.js';
|
|
3
|
+
import { readFileSync } from 'fs';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { dirname, join } from 'path';
|
|
3
6
|
|
|
4
7
|
export interface VersionInfo {
|
|
5
8
|
current: string;
|
|
@@ -9,7 +12,21 @@ export interface VersionInfo {
|
|
|
9
12
|
|
|
10
13
|
export class UpgradeManager {
|
|
11
14
|
private readonly packageName = 'appclean';
|
|
12
|
-
private
|
|
15
|
+
private currentVersion: string;
|
|
16
|
+
|
|
17
|
+
constructor() {
|
|
18
|
+
// Dynamically read version from package.json
|
|
19
|
+
try {
|
|
20
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
21
|
+
const __dirname = dirname(__filename);
|
|
22
|
+
const packagePath = join(__dirname, '../../package.json');
|
|
23
|
+
const packageData = JSON.parse(readFileSync(packagePath, 'utf-8'));
|
|
24
|
+
this.currentVersion = packageData.version || '2.0.0';
|
|
25
|
+
} catch (error) {
|
|
26
|
+
Logger.debug('Failed to read version from package.json, using fallback');
|
|
27
|
+
this.currentVersion = '2.0.0';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
13
30
|
|
|
14
31
|
/**
|
|
15
32
|
* Get version information from npm registry
|