diff --git a/src/cmd/root.go b/src/cmd/root.go index 247a519..7dca168 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -27,8 +27,8 @@ import ( ) var ( - EmbedViews embed.FS - EmbedViewsComponent embed.FS + EmbedIndex embed.FS + EmbedViews embed.FS ) // rootCmd represents the base command when called without any subcommands @@ -61,17 +61,16 @@ func runRest(_ *cobra.Command, _ []string) { log.Fatalln(err) } - engine := html.NewFileSystem(http.FS(EmbedViews), ".html") + engine := html.NewFileSystem(http.FS(EmbedIndex), ".html") engine.AddFunc("isEnableBasicAuth", func(token any) bool { return token != nil }) app := fiber.New(fiber.Config{ - Views: engine, - BodyLimit: 50 * 1024 * 1024, + Views: engine, }) app.Static("/statics", "./statics") app.Use("/components", filesystem.New(filesystem.Config{ - Root: http.FS(EmbedViewsComponent), + Root: http.FS(EmbedViews), PathPrefix: "views/components", Browse: true, })) @@ -141,9 +140,9 @@ func runRest(_ *cobra.Command, _ []string) { } // Execute adds all child commands to the root command and sets flags appropriately. -func Execute(embedViews embed.FS, embedViewsComponent embed.FS) { +func Execute(embedIndex embed.FS, embedViews embed.FS) { + EmbedIndex = embedIndex EmbedViews = embedViews - EmbedViewsComponent = embedViewsComponent if err := rootCmd.Execute(); err != nil { os.Exit(1) } diff --git a/src/go.mod b/src/go.mod index 5fc71ab..ce0fdef 100644 --- a/src/go.mod +++ b/src/go.mod @@ -18,7 +18,7 @@ require ( github.com/stretchr/testify v1.9.0 github.com/valyala/fasthttp v1.52.0 go.mau.fi/libsignal v0.1.0 - go.mau.fi/whatsmeow v0.0.0-20240308162537-79d9175fa09b + go.mau.fi/whatsmeow v0.0.0-20240312193055-9b989e1cc696 google.golang.org/protobuf v1.33.0 ) diff --git a/src/go.sum b/src/go.sum index 9adba98..1744731 100644 --- a/src/go.sum +++ b/src/go.sum @@ -96,6 +96,8 @@ go.mau.fi/util v0.4.0 h1:S2X3qU4pUcb/vxBRfAuZjbrR9xVMAXSjQojNBLPBbhs= go.mau.fi/util v0.4.0/go.mod h1:leeiHtgVBuN+W9aDii3deAXnfC563iN3WK6BF8/AjNw= go.mau.fi/whatsmeow v0.0.0-20240308162537-79d9175fa09b h1:Jk0/Lu6LdLD4kh0L9Q+06n7EefcS8ZDAKpwGeX7C4YQ= go.mau.fi/whatsmeow v0.0.0-20240308162537-79d9175fa09b/go.mod h1:lQHbhaG/fI+6hfGqz5Vzn2OBJBEZ05H0kCP6iJXriN4= +go.mau.fi/whatsmeow v0.0.0-20240312193055-9b989e1cc696 h1:F9ytx1yzfoclCS4DoEqQb1M0E5XpcX2j5dX8bIZjGFE= +go.mau.fi/whatsmeow v0.0.0-20240312193055-9b989e1cc696/go.mod h1:lQHbhaG/fI+6hfGqz5Vzn2OBJBEZ05H0kCP6iJXriN4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= diff --git a/src/main.go b/src/main.go index 80deb90..2441d04 100644 --- a/src/main.go +++ b/src/main.go @@ -6,11 +6,11 @@ import ( ) //go:embed views/index.html -var embedViews embed.FS +var embedIndex embed.FS //go:embed views -var embedViewsComponent embed.FS +var embedViews embed.FS func main() { - cmd.Execute(embedViews, embedViewsComponent) + cmd.Execute(embedIndex, embedViews) } diff --git a/src/views/components/AccountAvatar.js b/src/views/components/AccountAvatar.js new file mode 100644 index 0000000..2e602cb --- /dev/null +++ b/src/views/components/AccountAvatar.js @@ -0,0 +1,111 @@ +export default { + name: 'AccountAvatar', + data() { + return { + type: 'user', + phone: '', + image: null, + loading: false, + is_preview: false, + is_community: false, + } + }, + computed: { + phone_id() { + return this.type === 'user' ? `${this.phone}@${window.TYPEUSER}` : `${this.phone}@${window.TYPEGROUP}` + } + }, + methods: { + async openModal() { + this.handleReset(); + $('#modalUserAvatar').modal('show'); + }, + async handleSubmit() { + try { + await this.submitApi(); + showSuccessInfo("Avatar fetched") + } catch (err) { + showErrorInfo(err) + } + }, + async submitApi() { + this.loading = true; + try { + let response = await window.http.get(`/user/avatar?phone=${this.phone_id}&is_preview=${this.is_preview}&is_community=${this.is_community}`) + this.image = response.data.results.url; + } catch (error) { + if (error.response) { + throw new Error(error.response.data.message); + } + throw new Error(error.message); + } finally { + this.loading = false; + } + }, + handleReset() { + this.phone = ''; + this.image = null; + this.type = 'user'; + } + }, + template: ` +
+
+
Avatar
+
+ You can search someone avatar by phone +
+
+
+ + + + ` +} \ No newline at end of file diff --git a/src/views/components/AccountGroup.js b/src/views/components/AccountGroup.js new file mode 100644 index 0000000..a72ce12 --- /dev/null +++ b/src/views/components/AccountGroup.js @@ -0,0 +1,114 @@ +export default { + name: 'AccountGroup', + data() { + return { + groups: [] + } + }, + methods: { + async openModal() { + try { + this.dtClear() + await this.submitApi(); + $('#modalUserGroup').modal('show'); + this.dtRebuild() + showSuccessInfo("Groups fetched") + } catch (err) { + showErrorInfo(err) + } + }, + dtClear() { + $('#account_groups_table').DataTable().destroy(); + }, + dtRebuild() { + $('#account_groups_table').DataTable({ + "pageLength": 100, + "reloadData": true, + }).draw(); + }, + async handleLeaveGroup(group_id) { + try { + const ok = confirm("Are you sure to leave this group?"); + if (!ok) return; + + await this.leaveGroupApi(group_id); + this.dtClear() + await this.submitApi(); + this.dtRebuild() + showSuccessInfo("Group left") + } catch (err) { + showErrorInfo(err) + } + }, + async leaveGroupApi(group_id) { + try { + let payload = new FormData(); + payload.append("group_id", group_id) + await window.http.post(`/group/leave`, payload) + } catch (error) { + if (error.response) { + throw new Error(error.response.data.message); + } + throw new Error(error.message); + + } + }, + async submitApi() { + try { + let response = await window.http.get(`/user/my/groups`) + this.groups = response.data.results.data; + } catch (error) { + if (error.response) { + throw new Error(error.response.data.message); + } + throw new Error(error.message); + } + }, + formatDate: function (value) { + if (!value) return '' + return moment(value).format('LLL'); + } + }, + template: ` +
+
+
My List Groups
+
+ Display all groups you joined +
+
+
+ + + + ` +} \ No newline at end of file diff --git a/src/views/components/AccountPrivacy.js b/src/views/components/AccountPrivacy.js new file mode 100644 index 0000000..46061b8 --- /dev/null +++ b/src/views/components/AccountPrivacy.js @@ -0,0 +1,57 @@ +export default { + name: 'AccountPrivacy', + data() { + return { + data_privacy: null + } + }, + methods: { + async openModal() { + try { + await this.submitApi(); + $('#modalUserPrivacy').modal('show'); + showSuccessInfo("Privacy fetched") + } catch (err) { + showErrorInfo(err) + } + }, + async submitApi() { + try { + let response = await window.http.get(`/user/my/privacy`) + this.data_privacy = response.data.results; + } catch (error) { + if (error.response) { + throw new Error(error.response.data.message); + } + throw new Error(error.message); + } + }, + }, + template: ` +
+
+
My Privacy Setting
+
+ Get your privacy settings +
+
+
+ + + + ` +} \ No newline at end of file diff --git a/src/views/components/AccountUserInfo.js b/src/views/components/AccountUserInfo.js new file mode 100644 index 0000000..4bc2aa0 --- /dev/null +++ b/src/views/components/AccountUserInfo.js @@ -0,0 +1,112 @@ +export default { + name: 'AccountUserInfo', + data() { + return { + type: 'user', + phone: '', + // + name: null, + status: null, + devices: [], + // + loading: false, + } + }, + + computed: { + phone_id() { + return this.type === 'user' ? `${this.phone}@${window.TYPEUSER}` : `${this.phone}@${window.TYPEGROUP}` + } + }, + methods: { + async openModal() { + this.handleReset(); + $('#modalUserInfo').modal('show'); + }, + async handleSubmit() { + try { + await this.submitApi(); + showSuccessInfo("Info fetched") + } catch (err) { + showErrorInfo(err) + } + }, + async submitApi() { + this.loading = true; + try { + let response = await window.http.get(`/user/info?phone=${this.phone_id}`) + this.name = response.data.results.verified_name; + this.status = response.data.results.status; + this.devices = response.data.results.devices; + } catch (error) { + if (error.response) { + throw new Error(error.response.data.message); + } + throw new Error(error.message); + } finally { + this.loading = false; + } + }, + handleReset() { + this.phone = ''; + this.name = null; + this.status = null; + this.devices = []; + this.type = 'user'; + } + }, + template: ` +
+
+
User Info
+
+ You can search someone user info by phone +
+
+
+ + + + + ` +} \ No newline at end of file diff --git a/src/views/components/AppLogin.js b/src/views/components/AppLogin.js new file mode 100644 index 0000000..b276e7f --- /dev/null +++ b/src/views/components/AppLogin.js @@ -0,0 +1,72 @@ +export default { + name: 'AppLogin', + data() { + return { + login_link: '', + login_duration_sec: 0, + } + }, + methods: { + async openModal() { + try { + await this.submitApi(); + $('#modalLogin').modal({ + onApprove: function () { + return false; + } + }).modal('show'); + } catch (err) { + showErrorInfo(err) + } + }, + async submitApi() { + try { + let response = await window.http.get(`/app/login`) + let results = response.data.results; + this.login_link = results.qr_link; + this.login_duration_sec = results.qr_duration; + } catch (error) { + if (error.response) { + throw Error(error.response.data.message) + } + throw Error(error.message) + } + } + }, + template: ` +
+
+
Login
+
+ Scan your QRCode and you can use all this API feature +
+
+
+ + + + ` +} \ No newline at end of file diff --git a/src/views/components/AppLogout.js b/src/views/components/AppLogout.js new file mode 100644 index 0000000..f0ed901 --- /dev/null +++ b/src/views/components/AppLogout.js @@ -0,0 +1,38 @@ +export default { + name: 'AppLogout', + methods: { + async handleSubmit() { + try { + await this.submitApi() + showSuccessInfo("Logout success") + + // fetch devices + this.$emit('reload-devices') + } catch (err) { + showErrorInfo(err) + } + }, + + async submitApi() { + try { + await http.get(`/app/logout`) + } catch (error) { + if (error.response) { + throw Error(error.response.data.message) + } + throw Error(error.message) + } + + } + }, + template: ` +
+
+
Logout
+
+ Remove your login session in application +
+
+
+ ` +} \ No newline at end of file diff --git a/src/views/components/AppReconnect.js b/src/views/components/AppReconnect.js new file mode 100644 index 0000000..dcc507b --- /dev/null +++ b/src/views/components/AppReconnect.js @@ -0,0 +1,37 @@ +export default { + name: 'AppReconnect', + methods: { + async handleSubmit() { + try { + await this.submitApi() + showSuccessInfo("Reconnect success") + + // fetch devices + this.$emit('reload-devices') + } catch (err) { + showErrorInfo(err) + } + }, + async submitApi() { + try { + await window.http.get(`/app/reconnect`) + } catch (error) { + if (error.response) { + throw Error(error.response.data.message) + } + throw Error(error.message) + } + } + }, + template: ` +
+
+
Reconnect
+
+ Reconnect to whatsapp server, please do this if your api doesn't work or your application is down or + restart +
+
+
+ ` +} \ No newline at end of file diff --git a/src/views/components/MessageReact.js b/src/views/components/MessageReact.js new file mode 100644 index 0000000..d588965 --- /dev/null +++ b/src/views/components/MessageReact.js @@ -0,0 +1,111 @@ +export default { + name: 'ReactMessage', + data() { + return { + type: 'user', + phone: '', + message_id: '', + emoji: '', + loading: false, + } + }, + computed: { + phone_id() { + return this.type === 'user' ? `${this.phone}@${window.TYPEUSER}` : `${this.phone}@${window.TYPEGROUP}` + } + }, + methods: { + openModal() { + $('#modalMessageReaction').modal({ + onApprove: function () { + return false; + } + }).modal('show'); + }, + async handleSubmit() { + try { + let response = await this.submitApi() + showSuccessInfo(response) + $('#modalMessageReaction').modal('hide'); + } catch (err) { + showErrorInfo(err) + } + }, + async submitApi() { + this.loading = true; + try { + const payload = {phone: this.phone_id, emoji: this.emoji} + let response = await window.http.post(`/message/${this.message_id}/reaction`, payload) + this.handleReset(); + return response.data.message; + } catch (error) { + if (error.response) { + throw new Error(error.response.data.message); + } + throw new Error(error.message); + } finally { + this.loading = false; + } + }, + handleReset() { + this.phone = ''; + this.message_id = ''; + this.emoji = ''; + this.type = 'user'; + }, + }, + template: ` +
+
+ Message +
React Message
+
+ any message in private or group chat +
+
+
+ + + + + ` +} \ No newline at end of file diff --git a/src/views/components/MessageRevoke.js b/src/views/components/MessageRevoke.js new file mode 100644 index 0000000..959919b --- /dev/null +++ b/src/views/components/MessageRevoke.js @@ -0,0 +1,103 @@ +export default { + name: 'Message', + data() { + return { + type: 'user', + phone: '', + message_id: '', + loading: false, + } + }, + computed: { + phone_id() { + return this.type === 'user' ? `${this.phone}@${window.TYPEUSER}` : `${this.phone}@${window.TYPEGROUP}` + } + }, + methods: { + openModal() { + $('#modalMessageRevoke').modal({ + onApprove: function () { + return false; + } + }).modal('show'); + }, + async handleSubmit() { + try { + let response = await this.submitApi() + showSuccessInfo(response) + $('#modalMessageRevoke').modal('hide'); + } catch (err) { + showErrorInfo(err) + } + }, + async submitApi() { + this.loading = true; + try { + const payload = {phone: this.phone_id} + let response = await window.http.post(`/message/${this.message_id}/revoke`, payload) + this.handleReset(); + return response.data.message; + } catch (error) { + if (error.response) { + throw new Error(error.response.data.message); + } + throw new Error(error.message); + } finally { + this.loading = false; + } + }, + handleReset() { + this.phone = ''; + this.message_id = ''; + this.type = 'user'; + }, + }, + template: ` +
+
+ Message +
Revoke Message
+
+ any message in private or group chat +
+
+
+ + + + ` +} \ No newline at end of file diff --git a/src/views/components/MessageUpdate.js b/src/views/components/MessageUpdate.js index 6ca4644..0138b64 100644 --- a/src/views/components/MessageUpdate.js +++ b/src/views/components/MessageUpdate.js @@ -1,81 +1,74 @@ export default { - name: 'Update Message', + name: 'UpdateMessage', data() { return { - update_type: 'user', - update_phone: '', - update_message_id: '', - update_new_message: '', - update_loading: false, + type: 'user', + phone: '', + message_id: '', + new_message: '', + loading: false, } }, computed: { - update_phone_id() { - return this.update_type === 'user' ? `${this.update_phone}@${window.TYPEUSER}` : `${this.update_phone}@${window.TYPEGROUP}` + phone_id() { + return this.type === 'user' ? `${this.phone}@${window.TYPEUSER}` : `${this.phone}@${window.TYPEGROUP}` } }, methods: { - messageEditModal() { - $('#modalMessageEdit').modal({ + openModal() { + $('#modalMessageUpdate').modal({ onApprove: function () { return false; } }).modal('show'); }, - async messageEditProcess() { + async handleSubmit() { try { - let response = await this.messageEditApi() + let response = await this.submitApi() showSuccessInfo(response) - $('#modalMessageEdit').modal('hide'); + $('#modalMessageUpdate').modal('hide'); } catch (err) { showErrorInfo(err) } }, - messageEditApi() { - return new Promise(async (resolve, reject) => { - try { - this.update_loading = true; - - const payload = { - phone: this.update_phone_id, - message: this.update_new_message - } + async submitApi() { + this.loading = true; + try { + const payload = {phone: this.phone_id, message: this.new_message} - let response = await http.post(`/message/${this.update_message_id}/update`, payload) - this.messageEditReset(); - resolve(response.data.message) - } catch (error) { - if (error.response) { - reject(error.response.data.message) - } else { - reject(error.message) - } - } finally { - this.update_loading = false; + let response = await window.http.post(`/message/${this.message_id}/update`, payload) + this.handleReset(); + return response.data.message; + } catch (error) { + if (error.response) { + throw new Error(error.response.data.message); } - }) + throw new Error(error.message); + } finally { + this.loading = false; + } }, - messageEditReset() { - this.update_type = 'user'; - this.update_phone = ''; - this.update_message_id = ''; - this.update_new_message = ''; - this.update_loading = false; + handleReset() { + this.type = 'user'; + this.phone = ''; + this.message_id = ''; + this.new_message = ''; + this.loading = false; }, }, template: ` -
+
Message -
Edit Message
+
Update Message
Update your sent message
- -