updateflow 1.0.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.
- package/README.md +140 -0
- package/android-setup/MainActivity.java +91 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/updateflow.service.d.ts +46 -0
- package/dist/updateflow.service.d.ts.map +1 -0
- package/dist/updateflow.service.js +144 -0
- package/dist/updateflow.service.js.map +1 -0
- package/ios-setup/AppDelegate.swift +80 -0
- package/ios-setup/Main.storyboard +19 -0
- package/ios-setup/ViewController.swift +24 -0
- package/package.json +53 -0
- package/scripts/android-setup.js +79 -0
- package/scripts/ios-setup.js +70 -0
- package/scripts/postinstall.js +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# UpdateFlow
|
|
2
|
+
|
|
3
|
+
> **Live OTA updates for Ionic Capacitor apps — No App Store review needed.**
|
|
4
|
+
|
|
5
|
+
Update your app's HTML, CSS, JS — instantly. Without waiting for App Store or Play Store approval.
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/updateflow)
|
|
8
|
+
[](https://updateflow.in)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## How it works
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
You upload new zip → User opens app → Update downloads silently
|
|
16
|
+
→ User restarts app → New version active ✅
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### 1. Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install updateflow
|
|
27
|
+
npm install cordova-plugin-zip
|
|
28
|
+
npx cap sync
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 2. iOS setup (one command)
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
node node_modules/updateflow/scripts/ios-setup.js
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Then in Xcode: `Product → Clean Build Folder (Cmd+Shift+K)`
|
|
38
|
+
|
|
39
|
+
### 3. Android setup (one command)
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
node node_modules/updateflow/scripts/android-setup.js
|
|
43
|
+
ionic build && npx cap sync android
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 4. Add to your Angular app
|
|
47
|
+
|
|
48
|
+
**`app.module.ts`:**
|
|
49
|
+
```typescript
|
|
50
|
+
import { HttpClientModule } from '@angular/common/http';
|
|
51
|
+
|
|
52
|
+
@NgModule({
|
|
53
|
+
imports: [
|
|
54
|
+
HttpClientModule,
|
|
55
|
+
// ...
|
|
56
|
+
]
|
|
57
|
+
})
|
|
58
|
+
export class AppModule {}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**`app.component.ts`:**
|
|
62
|
+
```typescript
|
|
63
|
+
import { UpdateFlowService } from 'updateflow';
|
|
64
|
+
|
|
65
|
+
@Component({ ... })
|
|
66
|
+
export class AppComponent {
|
|
67
|
+
|
|
68
|
+
constructor(private updateFlow: UpdateFlowService) {}
|
|
69
|
+
|
|
70
|
+
async ngOnInit() {
|
|
71
|
+
await this.updateFlow.init({
|
|
72
|
+
apiUrl: 'https://YOUR_DASHBOARD/check_update.php',
|
|
73
|
+
apiKey: 'YOUR_API_KEY', // Get from UpdateFlow dashboard
|
|
74
|
+
debug: false
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Releasing an Update
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# 1. Build your app
|
|
86
|
+
ionic build
|
|
87
|
+
|
|
88
|
+
# 2. Create zip from www folder
|
|
89
|
+
cd www && zip -r ../update_2.0.zip . && cd ..
|
|
90
|
+
|
|
91
|
+
# 3. Upload zip to your UpdateFlow dashboard
|
|
92
|
+
# 4. Users will get the update on next app open
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Configuration
|
|
98
|
+
|
|
99
|
+
| Option | Type | Required | Default | Description |
|
|
100
|
+
|--------|------|----------|---------|-------------|
|
|
101
|
+
| `apiUrl` | `string` | ✅ | — | Your backend URL |
|
|
102
|
+
| `apiKey` | `string` | ✅ | — | API key from dashboard |
|
|
103
|
+
| `showAlerts` | `boolean` | ❌ | `true` | Show update alerts |
|
|
104
|
+
| `debug` | `boolean` | ❌ | `false` | Enable console logs |
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## API
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
// Check for updates manually
|
|
112
|
+
this.updateFlow.checkNow();
|
|
113
|
+
|
|
114
|
+
// Get current version
|
|
115
|
+
const version = this.updateFlow.getVersion();
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Platform Support
|
|
121
|
+
|
|
122
|
+
| Platform | Status |
|
|
123
|
+
|----------|--------|
|
|
124
|
+
| iOS | ✅ Supported |
|
|
125
|
+
| Android | ✅ Supported |
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Links
|
|
130
|
+
|
|
131
|
+
- 🌐 Dashboard: [updateflow.in](https://updateflow.in)
|
|
132
|
+
- 📖 Docs: [updateflow.in/docs](https://updateflow.in/docs)
|
|
133
|
+
- 💬 Support: admin@codecartel.co.in
|
|
134
|
+
- 🐛 Issues: [GitHub Issues](https://github.com/codecartel-in/updateflow/issues)
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## License
|
|
139
|
+
|
|
140
|
+
MIT © [CodeCartel](https://codecartel.co.in)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
package io.ionic.starter;
|
|
2
|
+
|
|
3
|
+
import android.content.SharedPreferences;
|
|
4
|
+
import android.os.Bundle;
|
|
5
|
+
import android.util.Log;
|
|
6
|
+
|
|
7
|
+
import com.getcapacitor.BridgeActivity;
|
|
8
|
+
|
|
9
|
+
import java.io.File;
|
|
10
|
+
|
|
11
|
+
public class MainActivity extends BridgeActivity {
|
|
12
|
+
|
|
13
|
+
private static final String TAG = "OTA";
|
|
14
|
+
|
|
15
|
+
// FIX: Yeh keys Angular service se match karni chahiye
|
|
16
|
+
private static final String KEY_VERSION = "app_current_version"; // ← Angular service wali key
|
|
17
|
+
private static final String KEY_PATH = "local_ota_path";
|
|
18
|
+
|
|
19
|
+
@Override
|
|
20
|
+
public void onCreate(Bundle savedInstanceState) {
|
|
21
|
+
super.onCreate(savedInstanceState);
|
|
22
|
+
applyOtaUpdateIfAvailable();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@Override
|
|
26
|
+
public void onResume() {
|
|
27
|
+
super.onResume();
|
|
28
|
+
applyOtaUpdateIfAvailable();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private void applyOtaUpdateIfAvailable() {
|
|
32
|
+
try {
|
|
33
|
+
// Capacitor Preferences "CapacitorStorage" mein save karta hai
|
|
34
|
+
SharedPreferences prefs = getSharedPreferences("CapacitorStorage", MODE_PRIVATE);
|
|
35
|
+
|
|
36
|
+
// FIX: Key name match karo Angular service se
|
|
37
|
+
String otaVersion = prefs.getString(KEY_VERSION, null);
|
|
38
|
+
|
|
39
|
+
if (otaVersion == null) {
|
|
40
|
+
Log.i(TAG, "No OTA version saved — standard boot.");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
Log.i(TAG, "OTA version found: " + otaVersion);
|
|
45
|
+
|
|
46
|
+
// Path build karo
|
|
47
|
+
String otaFolderPath = getFilesDir().getAbsolutePath() + "/updates/" + otaVersion;
|
|
48
|
+
File indexFile = new File(otaFolderPath + "/index.html");
|
|
49
|
+
File wwwIndexFile = new File(otaFolderPath + "/www/index.html");
|
|
50
|
+
|
|
51
|
+
Log.i(TAG, "Checking path: " + otaFolderPath);
|
|
52
|
+
|
|
53
|
+
if (wwwIndexFile.exists()) {
|
|
54
|
+
// www/index.html mila
|
|
55
|
+
otaFolderPath = otaFolderPath + "/www";
|
|
56
|
+
Log.i(TAG, "Found www/index.html");
|
|
57
|
+
} else if (indexFile.exists()) {
|
|
58
|
+
// root index.html mila
|
|
59
|
+
Log.i(TAG, "Found root index.html");
|
|
60
|
+
} else {
|
|
61
|
+
Log.w(TAG, "index.html nahi mila — clearing OTA, standard boot.");
|
|
62
|
+
clearOtaPreferences();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
setCapacitorServerPath(otaFolderPath);
|
|
67
|
+
Log.i(TAG, "OTA ACTIVE: v" + otaVersion + " -> " + otaFolderPath);
|
|
68
|
+
|
|
69
|
+
} catch (Exception e) {
|
|
70
|
+
Log.e(TAG, "OTA error: " + e.getMessage());
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private void setCapacitorServerPath(String path) {
|
|
75
|
+
getBridge().setServerBasePath(path);
|
|
76
|
+
|
|
77
|
+
getSharedPreferences("CapWebViewSettings", MODE_PRIVATE)
|
|
78
|
+
.edit().putString("serverBasePath", path).apply();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private void clearOtaPreferences() {
|
|
82
|
+
getSharedPreferences("CapacitorStorage", MODE_PRIVATE)
|
|
83
|
+
.edit()
|
|
84
|
+
.remove(KEY_VERSION)
|
|
85
|
+
.remove(KEY_PATH)
|
|
86
|
+
.apply();
|
|
87
|
+
|
|
88
|
+
getSharedPreferences("CapWebViewSettings", MODE_PRIVATE)
|
|
89
|
+
.edit().remove("serverBasePath").apply();
|
|
90
|
+
}
|
|
91
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UpdateFlowService = void 0;
|
|
4
|
+
var updateflow_service_1 = require("./updateflow.service");
|
|
5
|
+
Object.defineProperty(exports, "UpdateFlowService", { enumerable: true, get: function () { return updateflow_service_1.UpdateFlowService; } });
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,2DAAyD;AAAhD,uHAAA,iBAAiB,OAAA"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { HttpClient } from '@angular/common/http';
|
|
2
|
+
import { Platform } from '@ionic/angular';
|
|
3
|
+
export interface UpdateFlowConfig {
|
|
4
|
+
/** UpdateFlow dashboard se API URL — Required */
|
|
5
|
+
apiUrl: string;
|
|
6
|
+
/** Dashboard se milne wali unique API key — Required */
|
|
7
|
+
apiKey: string;
|
|
8
|
+
/** Update ke baad alert dikhaye? Default: true */
|
|
9
|
+
showAlerts?: boolean;
|
|
10
|
+
/** Debug logs? Default: false */
|
|
11
|
+
debug?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface UpdateInfo {
|
|
14
|
+
update_available: boolean;
|
|
15
|
+
new_version?: string;
|
|
16
|
+
url?: string;
|
|
17
|
+
is_critical?: boolean;
|
|
18
|
+
release_notes?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare class UpdateFlowService {
|
|
21
|
+
private http;
|
|
22
|
+
private platform;
|
|
23
|
+
currentVersion: string;
|
|
24
|
+
private config;
|
|
25
|
+
private readonly KEY_VERSION;
|
|
26
|
+
private readonly KEY_PATH;
|
|
27
|
+
constructor(http: HttpClient, platform: Platform);
|
|
28
|
+
/**
|
|
29
|
+
* App start par call karo — app.component.ts ke ngOnInit mein
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* await this.updateFlow.init({
|
|
33
|
+
* apiUrl: 'https://YOUR-DASHBOARD/check_update.php',
|
|
34
|
+
* apiKey: 'YOUR_API_KEY'
|
|
35
|
+
* });
|
|
36
|
+
*/
|
|
37
|
+
init(config: UpdateFlowConfig): Promise<void>;
|
|
38
|
+
private checkForUpdate;
|
|
39
|
+
private downloadAndApply;
|
|
40
|
+
/** Current version return karta hai */
|
|
41
|
+
getVersion(): string;
|
|
42
|
+
/** Manually update check trigger karo */
|
|
43
|
+
checkNow(): void;
|
|
44
|
+
private log;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=updateflow.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"updateflow.service.d.ts","sourceRoot":"","sources":["../src/updateflow.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGlD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAK1C,MAAM,WAAW,gBAAgB;IAC/B,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iCAAiC;IACjC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBACa,iBAAiB;IAU1B,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,QAAQ;IATX,cAAc,SAAM;IAC3B,OAAO,CAAC,MAAM,CAAoB;IAGlC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAyB;IACrD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuB;gBAGtC,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,QAAQ;IAG5B;;;;;;;;OAQG;IACU,IAAI,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IA0B1D,OAAO,CAAC,cAAc;YAwBR,gBAAgB;IAuD9B,uCAAuC;IAChC,UAAU,IAAI,MAAM;IAE3B,yCAAyC;IAClC,QAAQ,IAAI,IAAI;IAIvB,OAAO,CAAC,GAAG;CAGZ"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var _a;
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.UpdateFlowService = void 0;
|
|
14
|
+
const core_1 = require("@angular/core");
|
|
15
|
+
const http_1 = require("@angular/common/http");
|
|
16
|
+
const filesystem_1 = require("@capacitor/filesystem");
|
|
17
|
+
const preferences_1 = require("@capacitor/preferences");
|
|
18
|
+
const angular_1 = require("@ionic/angular");
|
|
19
|
+
const app_1 = require("@capacitor/app");
|
|
20
|
+
let UpdateFlowService = class UpdateFlowService {
|
|
21
|
+
constructor(http, platform) {
|
|
22
|
+
this.http = http;
|
|
23
|
+
this.platform = platform;
|
|
24
|
+
this.currentVersion = '';
|
|
25
|
+
// iOS AppDelegate + Android MainActivity se match karne wali keys
|
|
26
|
+
this.KEY_VERSION = 'app_current_version';
|
|
27
|
+
this.KEY_PATH = 'local_ota_path';
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* App start par call karo — app.component.ts ke ngOnInit mein
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* await this.updateFlow.init({
|
|
34
|
+
* apiUrl: 'https://YOUR-DASHBOARD/check_update.php',
|
|
35
|
+
* apiKey: 'YOUR_API_KEY'
|
|
36
|
+
* });
|
|
37
|
+
*/
|
|
38
|
+
async init(config) {
|
|
39
|
+
this.config = Object.assign({ showAlerts: true, debug: false }, config);
|
|
40
|
+
if (!this.platform.is('hybrid')) {
|
|
41
|
+
this.log('Browser mode — UpdateFlow skipped.');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (!config.apiKey || !config.apiUrl) {
|
|
45
|
+
console.error('[UpdateFlow] apiUrl aur apiKey dono required hain!');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const { value } = await preferences_1.Preferences.get({ key: this.KEY_VERSION });
|
|
50
|
+
const appInfo = await app_1.App.getInfo();
|
|
51
|
+
this.currentVersion = value || appInfo.version;
|
|
52
|
+
this.log('Version: ' + this.currentVersion);
|
|
53
|
+
this.checkForUpdate();
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
console.error('[UpdateFlow] Init error:', e);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
checkForUpdate() {
|
|
60
|
+
const url = `${this.config.apiUrl}?current_version=${this.currentVersion}&api_key=${this.config.apiKey}`;
|
|
61
|
+
this.http.get(url).subscribe({
|
|
62
|
+
next: async (res) => {
|
|
63
|
+
var _a;
|
|
64
|
+
if ((res === null || res === void 0 ? void 0 : res.update_available) && res.new_version && res.url) {
|
|
65
|
+
this.log('New version: ' + res.new_version);
|
|
66
|
+
await this.downloadAndApply(res.url, res.new_version, (_a = res.is_critical) !== null && _a !== void 0 ? _a : false);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
this.log('Already up to date.');
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
error: (err) => {
|
|
73
|
+
if (err.status === 401) {
|
|
74
|
+
console.error('[UpdateFlow] Invalid API key');
|
|
75
|
+
}
|
|
76
|
+
else if (err.status === 403) {
|
|
77
|
+
console.error('[UpdateFlow] Account suspended');
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
console.error('[UpdateFlow] Server error:', err.message);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
async downloadAndApply(zipUrl, newVersion, isCritical) {
|
|
86
|
+
try {
|
|
87
|
+
this.log('Downloading v' + newVersion + '...');
|
|
88
|
+
const download = await filesystem_1.Filesystem.downloadFile({
|
|
89
|
+
url: zipUrl,
|
|
90
|
+
path: `updateflow_${newVersion}.zip`,
|
|
91
|
+
directory: filesystem_1.Directory.Data
|
|
92
|
+
});
|
|
93
|
+
const targetUri = await filesystem_1.Filesystem.getUri({
|
|
94
|
+
directory: filesystem_1.Directory.Data,
|
|
95
|
+
path: `updates/${newVersion}`
|
|
96
|
+
});
|
|
97
|
+
this.log('Extracting...');
|
|
98
|
+
zip.unzip(download.path, targetUri.uri, async (result) => {
|
|
99
|
+
if (result === 0) {
|
|
100
|
+
this.log('Done! v' + newVersion + ' ready.');
|
|
101
|
+
await preferences_1.Preferences.set({ key: this.KEY_VERSION, value: newVersion });
|
|
102
|
+
await preferences_1.Preferences.set({
|
|
103
|
+
key: this.KEY_PATH,
|
|
104
|
+
value: targetUri.uri.replace('file://', '')
|
|
105
|
+
});
|
|
106
|
+
this.currentVersion = newVersion;
|
|
107
|
+
await filesystem_1.Filesystem.deleteFile({
|
|
108
|
+
path: `updateflow_${newVersion}.zip`,
|
|
109
|
+
directory: filesystem_1.Directory.Data
|
|
110
|
+
}).catch(() => { });
|
|
111
|
+
if (this.config.showAlerts) {
|
|
112
|
+
alert(isCritical
|
|
113
|
+
? 'Zaruri update install hua! App band karke dobara kholen.'
|
|
114
|
+
: 'Naya update install hua. App restart karein.');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
console.error('[UpdateFlow] Extraction failed:', result);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
console.error('[UpdateFlow] Error:', (e === null || e === void 0 ? void 0 : e.message) || e);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/** Current version return karta hai */
|
|
127
|
+
getVersion() { return this.currentVersion; }
|
|
128
|
+
/** Manually update check trigger karo */
|
|
129
|
+
checkNow() {
|
|
130
|
+
if (this.currentVersion)
|
|
131
|
+
this.checkForUpdate();
|
|
132
|
+
}
|
|
133
|
+
log(msg) {
|
|
134
|
+
var _a;
|
|
135
|
+
if ((_a = this.config) === null || _a === void 0 ? void 0 : _a.debug)
|
|
136
|
+
console.log('[UpdateFlow]', msg);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
exports.UpdateFlowService = UpdateFlowService;
|
|
140
|
+
exports.UpdateFlowService = UpdateFlowService = __decorate([
|
|
141
|
+
(0, core_1.Injectable)({ providedIn: 'root' }),
|
|
142
|
+
__metadata("design:paramtypes", [typeof (_a = typeof http_1.HttpClient !== "undefined" && http_1.HttpClient) === "function" ? _a : Object, angular_1.Platform])
|
|
143
|
+
], UpdateFlowService);
|
|
144
|
+
//# sourceMappingURL=updateflow.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"updateflow.service.js","sourceRoot":"","sources":["../src/updateflow.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,wCAA2C;AAC3C,+CAAkD;AAClD,sDAA8D;AAC9D,wDAAqD;AACrD,4CAA0C;AAC1C,wCAAqC;AAwB9B,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAS5B,YACU,IAAgB,EAChB,QAAkB;QADlB,SAAI,GAAJ,IAAI,CAAY;QAChB,aAAQ,GAAR,QAAQ,CAAU;QATrB,mBAAc,GAAG,EAAE,CAAC;QAG3B,kEAAkE;QACjD,gBAAW,GAAG,qBAAqB,CAAC;QACpC,aAAQ,GAAM,gBAAgB,CAAC;IAK7C,CAAC;IAEJ;;;;;;;;OAQG;IACI,KAAK,CAAC,IAAI,CAAC,MAAwB;QACxC,IAAI,CAAC,MAAM,mBAAK,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,IAAK,MAAM,CAAE,CAAC;QAE5D,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC;YAChC,IAAI,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;YAC/C,OAAO;QACT,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACrC,OAAO,CAAC,KAAK,CAAC,oDAAoD,CAAC,CAAC;YACpE,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,yBAAW,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;YACnE,MAAM,OAAO,GAAK,MAAM,SAAG,CAAC,OAAO,EAAE,CAAC;YAEtC,IAAI,CAAC,cAAc,GAAG,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC;YAC/C,IAAI,CAAC,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC;YAE5C,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAE,CAAC,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAEO,cAAc;QACpB,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,oBAAoB,IAAI,CAAC,cAAc,YAAY,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QAEzG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAa,GAAG,CAAC,CAAC,SAAS,CAAC;YACvC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;;gBAClB,IAAI,CAAA,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,gBAAgB,KAAI,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;oBACxD,IAAI,CAAC,GAAG,CAAC,eAAe,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC;oBAC5C,MAAM,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,WAAW,EAAE,MAAA,GAAG,CAAC,WAAW,mCAAI,KAAK,CAAC,CAAC;gBAClF,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;gBAClC,CAAC;YACH,CAAC;YACD,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE;gBACb,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;oBACvB,OAAO,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;gBAChD,CAAC;qBAAM,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;oBAC9B,OAAO,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;gBAClD,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;gBAC3D,CAAC;YACH,CAAC;SACF,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAC5B,MAAc,EACd,UAAkB,EAClB,UAAmB;QAEnB,IAAI,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,eAAe,GAAG,UAAU,GAAG,KAAK,CAAC,CAAC;YAE/C,MAAM,QAAQ,GAAG,MAAM,uBAAU,CAAC,YAAY,CAAC;gBAC7C,GAAG,EAAE,MAAM;gBACX,IAAI,EAAE,cAAc,UAAU,MAAM;gBACpC,SAAS,EAAE,sBAAS,CAAC,IAAI;aAC1B,CAAC,CAAC;YAEH,MAAM,SAAS,GAAG,MAAM,uBAAU,CAAC,MAAM,CAAC;gBACxC,SAAS,EAAE,sBAAS,CAAC,IAAI;gBACzB,IAAI,EAAE,WAAW,UAAU,EAAE;aAC9B,CAAC,CAAC;YAEH,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;YAE1B,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,GAAG,EAAE,KAAK,EAAE,MAAc,EAAE,EAAE;gBAC/D,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;oBACjB,IAAI,CAAC,GAAG,CAAC,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC,CAAC;oBAE7C,MAAM,yBAAW,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;oBACpE,MAAM,yBAAW,CAAC,GAAG,CAAC;wBACpB,GAAG,EAAE,IAAI,CAAC,QAAQ;wBAClB,KAAK,EAAE,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;qBAC5C,CAAC,CAAC;oBAEH,IAAI,CAAC,cAAc,GAAG,UAAU,CAAC;oBAEjC,MAAM,uBAAU,CAAC,UAAU,CAAC;wBAC1B,IAAI,EAAE,cAAc,UAAU,MAAM;wBACpC,SAAS,EAAE,sBAAS,CAAC,IAAI;qBAC1B,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;oBAEnB,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;wBAC3B,KAAK,CAAC,UAAU;4BACd,CAAC,CAAC,0DAA0D;4BAC5D,CAAC,CAAC,8CAA8C,CACjD,CAAC;oBACJ,CAAC;gBAEH,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,MAAM,CAAC,CAAC;gBAC3D,CAAC;YACH,CAAC,CAAC,CAAC;QAEL,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YAChB,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,CAAA,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,OAAO,KAAI,CAAC,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IAED,uCAAuC;IAChC,UAAU,KAAa,OAAO,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;IAE3D,yCAAyC;IAClC,QAAQ;QACb,IAAI,IAAI,CAAC,cAAc;YAAE,IAAI,CAAC,cAAc,EAAE,CAAC;IACjD,CAAC;IAEO,GAAG,CAAC,GAAW;;QACrB,IAAI,MAAA,IAAI,CAAC,MAAM,0CAAE,KAAK;YAAE,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;IAC3D,CAAC;CACF,CAAA;AA3IY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,iBAAU,EAAC,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;yDAWjB,iBAAU,oBAAV,iBAAU,gCACN,kBAAQ;GAXjB,iBAAiB,CA2I7B"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
import Capacitor
|
|
3
|
+
|
|
4
|
+
@UIApplicationMain
|
|
5
|
+
class AppDelegate: UIResponder, UIApplicationDelegate {
|
|
6
|
+
|
|
7
|
+
var window: UIWindow?
|
|
8
|
+
|
|
9
|
+
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
|
10
|
+
|
|
11
|
+
let defaults = UserDefaults.standard
|
|
12
|
+
let fileManager = FileManager.default
|
|
13
|
+
|
|
14
|
+
guard let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
|
|
15
|
+
return true
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let updatesFolderURL = documentsURL.appendingPathComponent("updates", isDirectory: true)
|
|
19
|
+
|
|
20
|
+
var bestPath: String? = nil
|
|
21
|
+
var maxVer: Double = 0.0
|
|
22
|
+
|
|
23
|
+
if fileManager.fileExists(atPath: updatesFolderURL.path) {
|
|
24
|
+
do {
|
|
25
|
+
let folders = try fileManager.contentsOfDirectory(
|
|
26
|
+
at: updatesFolderURL,
|
|
27
|
+
includingPropertiesForKeys: nil,
|
|
28
|
+
options: [.skipsHiddenFiles]
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
for folderURL in folders {
|
|
32
|
+
let versionStr = folderURL.lastPathComponent
|
|
33
|
+
.lowercased()
|
|
34
|
+
.replacingOccurrences(of: "v", with: "")
|
|
35
|
+
|
|
36
|
+
guard let vDouble = Double(versionStr), vDouble >= maxVer else { continue }
|
|
37
|
+
|
|
38
|
+
let wwwIndex = folderURL.appendingPathComponent("www/index.html")
|
|
39
|
+
let rootIndex = folderURL.appendingPathComponent("index.html")
|
|
40
|
+
|
|
41
|
+
if fileManager.fileExists(atPath: wwwIndex.path) {
|
|
42
|
+
maxVer = vDouble
|
|
43
|
+
bestPath = folderURL.appendingPathComponent("www").path
|
|
44
|
+
} else if fileManager.fileExists(atPath: rootIndex.path) {
|
|
45
|
+
maxVer = vDouble
|
|
46
|
+
bestPath = folderURL.path
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
} catch {
|
|
50
|
+
print("[OTA] Error scanning updates folder: \(error)")
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if let target = bestPath {
|
|
55
|
+
defaults.set(target, forKey: "serverBasePath")
|
|
56
|
+
defaults.synchronize()
|
|
57
|
+
print("[OTA] Active: v\(maxVer) from \(target)")
|
|
58
|
+
} else {
|
|
59
|
+
defaults.removeObject(forKey: "serverBasePath")
|
|
60
|
+
defaults.synchronize()
|
|
61
|
+
print("[OTA] Standard boot — no update found.")
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return true
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
|
|
68
|
+
return ApplicationDelegateProxy.shared.application(app, open: url, options: options)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
|
|
72
|
+
return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
func applicationWillResignActive(_ application: UIApplication) { }
|
|
76
|
+
func applicationDidEnterBackground(_ application: UIApplication) { }
|
|
77
|
+
func applicationWillEnterForeground(_ application: UIApplication) { }
|
|
78
|
+
func applicationDidBecomeActive(_ application: UIApplication) { }
|
|
79
|
+
func applicationWillTerminate(_ application: UIApplication) { }
|
|
80
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14111" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
|
3
|
+
<device id="retina4_7" orientation="portrait">
|
|
4
|
+
<adaptation id="fullscreen"/>
|
|
5
|
+
</device>
|
|
6
|
+
<dependencies>
|
|
7
|
+
<deployment identifier="iOS"/>
|
|
8
|
+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
|
|
9
|
+
</dependencies>
|
|
10
|
+
<scenes>
|
|
11
|
+
<!--OTA Bridge View Controller-->
|
|
12
|
+
<scene sceneID="tne-QT-ifu">
|
|
13
|
+
<objects>
|
|
14
|
+
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="App" sceneMemberID="viewController"/>
|
|
15
|
+
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
|
16
|
+
</objects>
|
|
17
|
+
</scene>
|
|
18
|
+
</scenes>
|
|
19
|
+
</document>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
import Capacitor
|
|
3
|
+
|
|
4
|
+
@objc(ViewController)
|
|
5
|
+
class ViewController: CAPBridgeViewController {
|
|
6
|
+
|
|
7
|
+
override func viewDidLoad() {
|
|
8
|
+
super.viewDidLoad()
|
|
9
|
+
|
|
10
|
+
if let otaPath = UserDefaults.standard.string(forKey: "serverBasePath") {
|
|
11
|
+
let indexPath = otaPath + "/index.html"
|
|
12
|
+
|
|
13
|
+
if FileManager.default.fileExists(atPath: indexPath) {
|
|
14
|
+
self.setServerBasePath(path: otaPath)
|
|
15
|
+
print("[OTA] ViewController: Path applied -> \(otaPath)")
|
|
16
|
+
} else {
|
|
17
|
+
UserDefaults.standard.removeObject(forKey: "serverBasePath")
|
|
18
|
+
print("[OTA] ViewController: Path invalid, standard boot.")
|
|
19
|
+
}
|
|
20
|
+
} else {
|
|
21
|
+
print("[OTA] ViewController: Standard boot.")
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "updateflow",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Live OTA updates for Ionic Capacitor apps — iOS & Android. No App Store needed.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist/",
|
|
9
|
+
"ios-setup/",
|
|
10
|
+
"android-setup/",
|
|
11
|
+
"scripts/",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc --project tsconfig.json",
|
|
16
|
+
"postinstall": "node scripts/postinstall.js"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"ionic",
|
|
20
|
+
"capacitor",
|
|
21
|
+
"ota",
|
|
22
|
+
"live-update",
|
|
23
|
+
"ios",
|
|
24
|
+
"android",
|
|
25
|
+
"hot-update",
|
|
26
|
+
"app-update",
|
|
27
|
+
"over-the-air",
|
|
28
|
+
"updateflow"
|
|
29
|
+
],
|
|
30
|
+
"author": "CodeCartel <admin@codecartel.co.in> (https://codecartel.co.in)",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"homepage": "https://updateflow.in",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/codecartel-in/updateflow"
|
|
36
|
+
},
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/codecartel-in/updateflow/issues",
|
|
39
|
+
"email": "admin@codecartel.co.in"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"@angular/common": ">=12.0.0",
|
|
43
|
+
"@angular/core": ">=12.0.0",
|
|
44
|
+
"@capacitor/app": ">=1.0.0",
|
|
45
|
+
"@capacitor/core": ">=3.0.0",
|
|
46
|
+
"@capacitor/filesystem": ">=1.0.0",
|
|
47
|
+
"@capacitor/preferences": ">=1.0.0",
|
|
48
|
+
"@ionic/angular": ">=6.0.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"typescript": "^5.9.3"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const GREEN = '\x1b[32m';
|
|
7
|
+
const YELLOW = '\x1b[33m';
|
|
8
|
+
const RED = '\x1b[31m';
|
|
9
|
+
const CYAN = '\x1b[36m';
|
|
10
|
+
const RESET = '\x1b[0m';
|
|
11
|
+
const log = (c, i, m) => console.log(`${c}${i} ${m}${RESET}`);
|
|
12
|
+
|
|
13
|
+
const PKG_ANDROID = path.join(__dirname, '..', 'android-setup');
|
|
14
|
+
const PROJ_ROOT = process.cwd();
|
|
15
|
+
|
|
16
|
+
function findMainActivity() {
|
|
17
|
+
const base = path.join(PROJ_ROOT, 'android', 'app', 'src', 'main', 'java');
|
|
18
|
+
if (!fs.existsSync(base)) return null;
|
|
19
|
+
|
|
20
|
+
function walk(dir, depth = 0) {
|
|
21
|
+
if (depth > 5) return null;
|
|
22
|
+
try {
|
|
23
|
+
const items = fs.readdirSync(dir);
|
|
24
|
+
for (const item of items) {
|
|
25
|
+
const full = path.join(dir, item);
|
|
26
|
+
if (fs.statSync(full).isDirectory()) {
|
|
27
|
+
if (fs.existsSync(path.join(full, 'MainActivity.java'))) {
|
|
28
|
+
return path.join(full, 'MainActivity.java');
|
|
29
|
+
}
|
|
30
|
+
const found = walk(full, depth + 1);
|
|
31
|
+
if (found) return found;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
} catch(e) {}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
return walk(base);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
console.log('\n' + CYAN + '⚡ UpdateFlow — Android Setup' + RESET);
|
|
41
|
+
console.log('─'.repeat(40));
|
|
42
|
+
|
|
43
|
+
const mainActivityPath = findMainActivity();
|
|
44
|
+
if (!mainActivityPath) {
|
|
45
|
+
log(RED, '✗', 'MainActivity.java nahi mili.');
|
|
46
|
+
log(YELLOW, '→', 'Run: ionic cap add android');
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
log(GREEN, '✓', 'Found: ' + mainActivityPath);
|
|
51
|
+
|
|
52
|
+
// Existing file se package naam nikalo
|
|
53
|
+
let packageName = '';
|
|
54
|
+
const existing = fs.readFileSync(mainActivityPath, 'utf8');
|
|
55
|
+
const match = existing.match(/^package\s+([\w.]+);/m);
|
|
56
|
+
if (match) {
|
|
57
|
+
packageName = match[1];
|
|
58
|
+
log(GREEN, '✓', 'Package naam: ' + packageName);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Backup
|
|
62
|
+
fs.copyFileSync(mainActivityPath, mainActivityPath + '.backup');
|
|
63
|
+
log(YELLOW, '⚠', 'Backup: MainActivity.java.backup');
|
|
64
|
+
|
|
65
|
+
// New file copy karo + package naam replace karo
|
|
66
|
+
let newContent = fs.readFileSync(path.join(PKG_ANDROID, 'MainActivity.java'), 'utf8');
|
|
67
|
+
if (packageName) {
|
|
68
|
+
newContent = newContent.replace(/^package\s+[\w.]+;/m, `package ${packageName};`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
fs.writeFileSync(mainActivityPath, newContent, 'utf8');
|
|
72
|
+
log(GREEN, '✓', 'MainActivity.java updated!');
|
|
73
|
+
|
|
74
|
+
console.log('─'.repeat(40));
|
|
75
|
+
log(GREEN, '✅', 'Android setup complete!');
|
|
76
|
+
console.log('\n Next steps:');
|
|
77
|
+
console.log(' ionic build');
|
|
78
|
+
console.log(' npx cap sync android');
|
|
79
|
+
console.log(' npx cap open android → Run\n');
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const GREEN = '\x1b[32m';
|
|
7
|
+
const YELLOW = '\x1b[33m';
|
|
8
|
+
const RED = '\x1b[31m';
|
|
9
|
+
const CYAN = '\x1b[36m';
|
|
10
|
+
const RESET = '\x1b[0m';
|
|
11
|
+
const log = (c, i, m) => console.log(`${c}${i} ${m}${RESET}`);
|
|
12
|
+
|
|
13
|
+
const PKG_IOS = path.join(__dirname, '..', 'ios-setup');
|
|
14
|
+
const PROJ_ROOT = process.cwd();
|
|
15
|
+
|
|
16
|
+
function findIosFolder() {
|
|
17
|
+
const paths = [
|
|
18
|
+
path.join(PROJ_ROOT, 'ios', 'App', 'App'),
|
|
19
|
+
path.join(PROJ_ROOT, 'ios', 'app', 'app'),
|
|
20
|
+
];
|
|
21
|
+
return paths.find(p => fs.existsSync(p)) || null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
console.log('\n' + CYAN + '⚡ UpdateFlow — iOS Setup' + RESET);
|
|
25
|
+
console.log('─'.repeat(40));
|
|
26
|
+
|
|
27
|
+
const iosFolder = findIosFolder();
|
|
28
|
+
if (!iosFolder) {
|
|
29
|
+
log(RED, '✗', 'ios/App/App folder nahi mila.');
|
|
30
|
+
log(YELLOW, '→', 'Run: ionic cap add ios');
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
log(GREEN, '✓', 'iOS folder found!');
|
|
35
|
+
|
|
36
|
+
const files = [
|
|
37
|
+
{ src: 'AppDelegate.swift', dest: 'AppDelegate.swift' },
|
|
38
|
+
{ src: 'ViewController.swift', dest: 'ViewController.swift' },
|
|
39
|
+
{ src: 'Main.storyboard', dest: 'Base.lproj/Main.storyboard' },
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
let ok = true;
|
|
43
|
+
for (const f of files) {
|
|
44
|
+
const src = path.join(PKG_IOS, f.src);
|
|
45
|
+
const dest = path.join(iosFolder, f.dest);
|
|
46
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
47
|
+
|
|
48
|
+
if (fs.existsSync(dest)) {
|
|
49
|
+
fs.copyFileSync(dest, dest + '.backup');
|
|
50
|
+
log(YELLOW, '⚠', 'Backup created: ' + f.dest + '.backup');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
fs.copyFileSync(src, dest);
|
|
55
|
+
log(GREEN, '✓', 'Copied: ' + f.dest);
|
|
56
|
+
} catch (e) {
|
|
57
|
+
log(RED, '✗', 'Failed: ' + f.dest);
|
|
58
|
+
ok = false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
console.log('─'.repeat(40));
|
|
63
|
+
if (ok) {
|
|
64
|
+
log(GREEN, '✅', 'iOS setup complete!');
|
|
65
|
+
console.log('\n Xcode mein:');
|
|
66
|
+
console.log(' Product → Clean Build Folder (Cmd+Shift+K)');
|
|
67
|
+
console.log(' Build karo aur iPhone par install karo\n');
|
|
68
|
+
} else {
|
|
69
|
+
log(RED, '✗', 'Kuch files copy nahi huin. Manually copy karo ios-setup/ se.');
|
|
70
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const CYAN = '\x1b[36m';
|
|
4
|
+
const GREEN = '\x1b[32m';
|
|
5
|
+
const YELLOW = '\x1b[33m';
|
|
6
|
+
const RESET = '\x1b[0m';
|
|
7
|
+
|
|
8
|
+
console.log('\n' + CYAN + '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━' + RESET);
|
|
9
|
+
console.log(CYAN + ' ⚡ UpdateFlow installed successfully!' + RESET);
|
|
10
|
+
console.log(CYAN + '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━' + RESET);
|
|
11
|
+
console.log('');
|
|
12
|
+
console.log(YELLOW + ' Next steps:' + RESET);
|
|
13
|
+
console.log('');
|
|
14
|
+
console.log(' 1️⃣ iOS setup:');
|
|
15
|
+
console.log(GREEN + ' node node_modules/updateflow/scripts/ios-setup.js' + RESET);
|
|
16
|
+
console.log('');
|
|
17
|
+
console.log(' 2️⃣ Android setup:');
|
|
18
|
+
console.log(GREEN + ' node node_modules/updateflow/scripts/android-setup.js' + RESET);
|
|
19
|
+
console.log('');
|
|
20
|
+
console.log(' 3️⃣ app.component.ts mein add karo:');
|
|
21
|
+
console.log(GREEN + ' import { UpdateFlowService } from \'updateflow\';' + RESET);
|
|
22
|
+
console.log('');
|
|
23
|
+
console.log(' 📖 Docs: https://updateflow.in/docs');
|
|
24
|
+
console.log(' 💬 Support: admin@codecartel.co.in');
|
|
25
|
+
console.log('');
|
|
26
|
+
console.log(CYAN + '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━' + RESET);
|
|
27
|
+
console.log('');
|