temporary 2/9/2025
This commit is contained in:
parent
bc5492eb12
commit
1710ce35d2
29
core/env.go
29
core/env.go
|
|
@ -12,15 +12,18 @@ import (
|
|||
// List of environment variables that the program understands.
|
||||
|
||||
var (
|
||||
TIN string
|
||||
CLIENT_ID string
|
||||
CLIENT_SECRET_1 string
|
||||
CLIENT_SECRET_2 string
|
||||
CERTIFICATE_FILENAME string // path to a PKCS12 file e.g. cert.p12, static/1f92.p12, etc
|
||||
CERTIFICATE_PASSWORD string // the PIN of the aforementioned PKCS12 file
|
||||
PORT string // the port the program listens to
|
||||
DEBUG bool = false // whether to use the debug or production environment
|
||||
LOCAL bool = false // whether we're doing local development or deploying for production
|
||||
TIN string
|
||||
CLIENT_ID string
|
||||
CLIENT_SECRET_1 string
|
||||
CLIENT_SECRET_2 string
|
||||
CERTIFICATE_FILENAME string // path to a PKCS12 file e.g. cert.p12, static/1f92.p12, etc
|
||||
CERTIFICATE_PASSWORD string // the PIN of the aforementioned PKCS12 file
|
||||
PORT string // the port the program listens to
|
||||
DEBUG bool = false // whether to use the debug or production environment
|
||||
|
||||
// Whether the development build is enabled or not. This adds extra code
|
||||
// paths and development-realted features.
|
||||
DEV bool = false
|
||||
IRALEX_BACKEND_BASE_URL string
|
||||
IRALEX_AUTH_EMAIL string // email address used to get an access token from the Iralex backend
|
||||
IRALEX_AUTH_PASSWORD string // password used to get an access token from the Iralex backend
|
||||
|
|
@ -53,7 +56,7 @@ func SetUpEnvironmentVariables() {
|
|||
|
||||
PORT = os.Getenv("PORT")
|
||||
_DEBUG := os.Getenv("DEBUG")
|
||||
_LOCAL := os.Getenv("LOCAL")
|
||||
_DEV := os.Getenv("DEV")
|
||||
|
||||
IRALEX_BACKEND_BASE_URL = os.Getenv("IRALEX_BACKEND_BASE_URL") // required
|
||||
IRALEX_AUTH_EMAIL = os.Getenv("IRALEX_AUTH_EMAIL") // optional
|
||||
|
|
@ -123,10 +126,10 @@ func SetUpEnvironmentVariables() {
|
|||
// add more here...
|
||||
}
|
||||
|
||||
if _LOCAL != "" {
|
||||
LOCAL, err = strconv.ParseBool(_LOCAL)
|
||||
if _DEV != "" {
|
||||
DEV, err = strconv.ParseBool(_DEV)
|
||||
if err != nil {
|
||||
LOCAL = false
|
||||
DEV = false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
qrcode "github.com/skip2/go-qrcode"
|
||||
)
|
||||
|
||||
/*
|
||||
Storage:
|
||||
|
||||
This file implements a file storage system on the local disk. Ultimately, all
|
||||
file handling in the application must go through this API, in order to maintain
|
||||
file operations easier.
|
||||
All the files and folders will be inside the storage/ directory, relative to the
|
||||
executable.
|
||||
|
||||
Dependencies: core/log
|
||||
*/
|
||||
|
||||
// Low-level API. Creates a new folder in the storage/ directory. Does nothing
|
||||
// if it already exists. Folder name must be a relative path i.e. should not
|
||||
// start with a slash (/).
|
||||
//
|
||||
// The created folders are categorized based on the year and the month. This
|
||||
// makes backing up the storage and version controlling it a lot easier.
|
||||
func CreateFolder(foldername string) {
|
||||
var yearAsString = strconv.Itoa(time.Now().UTC().Year())
|
||||
var monthAsString = fmt.Sprint(int(time.Now().UTC().Month()), "-", time.Now().UTC().Month())
|
||||
path := "storage/" + yearAsString + "/" + monthAsString + "/" + foldername
|
||||
|
||||
err := os.MkdirAll(path, os.ModePerm)
|
||||
if err != nil {
|
||||
// Occurs only if the path exists but is not a directory. Nothing
|
||||
// important to handle. Just a log is sufficient enough.
|
||||
LogInfo.Printf("WARNING: Couldn't create new folder %s - %v\n", path, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a QR code image with the specified name, from the specified link.
|
||||
//
|
||||
// Returns an error if the creation of the folder, or the creation of the QR
|
||||
// image fails.
|
||||
//
|
||||
// If the file already exists, overwrites the old one.
|
||||
//
|
||||
// The filename parameter should not include the file extension. If it does, the
|
||||
// actual file extension is appended e.g. specifying "qr.png" results in the
|
||||
// creation of a file named "qr.png.png".
|
||||
func CreateQRCodeImage(link, filename string) error {
|
||||
CreateFolder("qrcode")
|
||||
var yearAsString = strconv.Itoa(time.Now().UTC().Year())
|
||||
var monthAsString = fmt.Sprint(int(time.Now().UTC().Month()), "-", time.Now().UTC().Month())
|
||||
filename = "storage/" + yearAsString + "/" + monthAsString + "/qrcode/" + filename + ".png"
|
||||
|
||||
err := qrcode.WriteFile(link, qrcode.Medium, 256, filename)
|
||||
if err != nil {
|
||||
LogInfo.Printf("ERROR: Couldn't generate QRCode (link: %s, filename: %s) - %s", link, filename, err.Error())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
1
go.mod
1
go.mod
|
|
@ -12,6 +12,7 @@ require (
|
|||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||
golang.org/x/crypto v0.38.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -18,6 +18,8 @@ github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxU
|
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/wk8/go-ordered-map v1.0.0 h1:BV7z+2PaK8LTSd/mWgY12HyMAo5CEgkHqbkVq2thqr8=
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
package route
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"iralex-einvoice/config"
|
||||
"iralex-einvoice/core"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
|
|
@ -15,6 +12,8 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// TODO(pooya): Rename the following function to LHDNAuthenticate().
|
||||
|
||||
func AuthenticateFormData(ClientID string, ClientSecret string, TIN string) string {
|
||||
// Check if the cached access token is still valid, and then use it. The
|
||||
// LHDN auth API has a rate limit of 12 requests per minute, so we need to
|
||||
|
|
@ -78,40 +77,3 @@ func AuthenticateFormData(ClientID string, ClientSecret string, TIN string) stri
|
|||
|
||||
return accessToken
|
||||
}
|
||||
|
||||
func AuthenticateJSON(ClientID string, ClientSecret string, TIN string) {
|
||||
fmt.Println("Sending Request (Authenticate - JSON Body):\n-----------------")
|
||||
|
||||
body, _ := json.Marshal(map[string]string{
|
||||
"client_id": ClientID,
|
||||
"client_secret": ClientSecret,
|
||||
"grant_type": "client_credentials",
|
||||
"scope": "InvoicingAPI",
|
||||
})
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, config.LHDN_BASE_URL+"/connect/token", bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
log.Fatalf("Error in constructing the request: %v", err)
|
||||
}
|
||||
|
||||
req.Header.Add("Accept", "application/json")
|
||||
req.Header.Add("onbehalfof", TIN)
|
||||
|
||||
bytes, _ := httputil.DumpRequestOut(req, true)
|
||||
fmt.Printf("%s\n", bytes)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Error in sending request: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
out, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatalf("Error in reading response body: %v", err)
|
||||
}
|
||||
|
||||
sb := string(out)
|
||||
log.Print(sb)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"iralex-einvoice/config"
|
||||
"iralex-einvoice/core"
|
||||
"iralex-einvoice/route"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func HandleDebugQRCodeGenerate(w http.ResponseWriter, r *http.Request) {
|
||||
// filepath := "N:\\Pooya\\Projects\\iralex einvoice\\storage\\2025\\8-August\\qrcode\\408BMZ0D2AVGTWHDWAH3GQ0K10.png"
|
||||
// content, err := os.ReadFile(filepath)
|
||||
// if err != nil {
|
||||
// fmt.Println("failed to read file %v", err)
|
||||
// }
|
||||
// b := b64.StdEncoding.EncodeToString(content)
|
||||
// fmt.Println(b)
|
||||
// return
|
||||
|
||||
// hardcoded for testing
|
||||
// QR code generation must be parameterized on document UUID
|
||||
const docUUID = "408BMZ0D2AVGTWHDWAH3GQ0K10"
|
||||
var longID string = ""
|
||||
|
||||
accessToken := route.AuthenticateFormData(core.CLIENT_ID, core.CLIENT_SECRET_1, core.TIN)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, config.LHDN_BASE_URL+"/api/v1.0/documents/"+docUUID+"/raw", nil) // GetDocument
|
||||
if err != nil {
|
||||
fmt.Printf("Error in constructing the request: %v", err)
|
||||
return
|
||||
}
|
||||
req.Header.Add("Accept", "application/json")
|
||||
req.Header.Add("Authorization", "Bearer "+accessToken)
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: time.Second * config.DEFAULT_TIMEOUT,
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
fmt.Printf("couldn't send the request: %v", err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var respBody map[string]interface{}
|
||||
json.NewDecoder(resp.Body).Decode(&respBody)
|
||||
if resp.StatusCode == 200 {
|
||||
if _longID, ok := respBody["longID"]; ok {
|
||||
longID = _longID.(string)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Error\n")
|
||||
return
|
||||
}
|
||||
|
||||
var link = "https://myinvois.hasil.gov.my/" + docUUID + "/share/" + longID
|
||||
fmt.Println("Link")
|
||||
fmt.Println(link)
|
||||
fmt.Fprintf(w, "Link: %s\n", link)
|
||||
|
||||
err = core.CreateQRCodeImage(link, docUUID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "ERROR: Couldn't create the QRCode image: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
|
@ -30,12 +30,16 @@ func Run() {
|
|||
|
||||
http.HandleFunc("/ping", HandlePing)
|
||||
|
||||
if core.LOCAL {
|
||||
if core.DEV {
|
||||
http.HandleFunc("/submit", HandleSubmitDebug)
|
||||
} else {
|
||||
http.HandleFunc("/submit", HandleSubmit)
|
||||
}
|
||||
|
||||
if core.DEV { // Development-only routes
|
||||
http.HandleFunc("/debug/qrcode", HandleDebugQRCodeGenerate)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Listen on 0.0.0.0:PORT.
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 775 B |
Loading…
Reference in New Issue