temporary 2/9/2025

This commit is contained in:
Ali Arya 2025-09-02 15:39:47 +03:30
parent bc5492eb12
commit 1710ce35d2
10 changed files with 159 additions and 54 deletions

View File

@ -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
}
}

65
core/storage.go Normal file
View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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)
}

View File

@ -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())
}
}

View File

@ -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