first commit
This commit is contained in:
commit
eef066b179
37
.gitignore
vendored
Normal file
37
.gitignore
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/go,visualstudiocode
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=go,visualstudiocode
|
||||||
|
|
||||||
|
### Go ###
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
# vendor/
|
||||||
|
|
||||||
|
### Go Patch ###
|
||||||
|
/vendor/
|
||||||
|
/Godeps/
|
||||||
|
|
||||||
|
### VisualStudioCode ###
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
*.code-workspace
|
||||||
|
|
||||||
|
### VisualStudioCode Patch ###
|
||||||
|
# Ignore all local history of files
|
||||||
|
.history
|
||||||
|
.ionide
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/go,visualstudiocode
|
17
Dockerfile
Normal file
17
Dockerfile
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
FROM golang:1.16.5 as builder
|
||||||
|
|
||||||
|
WORKDIR /go-microservice/
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN CGO_ENABLED=0 go build -o microservice /go-microservice/main.go
|
||||||
|
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
WORKDIR /go-microservice
|
||||||
|
|
||||||
|
COPY --from=builder /go-microservice/ /go-microservice/
|
||||||
|
|
||||||
|
EXPOSE 9090
|
||||||
|
|
||||||
|
CMD ./microservice
|
16
README.md
Normal file
16
README.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Product Catalog API
|
||||||
|
|
||||||
|
Blogs for Detailed Tutorial:
|
||||||
|
|
||||||
|
* <a href="https://learnai1.home.blog/2021/03/15/microservices-in-go/">Introductory Blog.</a>
|
||||||
|
* <a href="https://learnai1.home.blog/2021/03/18/microservices-in-go-part-2/">Adding More API's.</a>
|
||||||
|
* <a href="https://learnai1.home.blog/2021/06/27/authentication-in-go-microservices/">Basic Authentication in Microservices.</a>
|
||||||
|
* <a href="https://learnai1.home.blog/2021/07/08/microservices-in-go-part-iv-docker-and-go-microservices/">Docker and Go Microservices.</a>
|
||||||
|
* <a href="https://learnai1.home.blog/2021/08/05/https-server-in-go/">HTTPS Server in Go.</a>
|
||||||
|
## Run API
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
go run .\main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the above command to run API on your local machine on port 9090.
|
30
data/data.json
Normal file
30
data/data.json
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "e7f31219-7986-11eb-bd65-98fa9b64e75b",
|
||||||
|
"name": "iPhone 12 Pro",
|
||||||
|
"description": "Pro variant of iPhone 12",
|
||||||
|
"price": 100000,
|
||||||
|
"isAvailable": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "f82fa5ec-7986-11eb-a8e3-98fa9b64e75b",
|
||||||
|
"name": "iPhone 12 Pro Max",
|
||||||
|
"description": "Pro max variant of iPhone 12",
|
||||||
|
"price": 120000,
|
||||||
|
"isAvailable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e7f35219-7986-11eb-bd65-98fa9b64e75b",
|
||||||
|
"name": "iPhone 12 Mini",
|
||||||
|
"description": "Mini variant of iPhone 12",
|
||||||
|
"price": 60000,
|
||||||
|
"isAvailable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e7f35219-8986-11eb-bd65-98fa9b64e75b",
|
||||||
|
"name": "iPhone 12",
|
||||||
|
"description": "Base variant of iPhone 12",
|
||||||
|
"price": 78000,
|
||||||
|
"isAvailable": true
|
||||||
|
}
|
||||||
|
]
|
123
entity/product.go
Normal file
123
entity/product.go
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
package entity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
//Product defines a structure for an item in product catalog
|
||||||
|
type Product struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Price float64 `json:"price"`
|
||||||
|
IsAvailable bool `json:"isAvailable"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrNoProduct is used if no product found
|
||||||
|
var ErrNoProduct = errors.New("no product found")
|
||||||
|
|
||||||
|
// GetProducts returns the JSON file content if available else returns an error.
|
||||||
|
func GetProducts() ([]byte, error) {
|
||||||
|
// Read JSON file
|
||||||
|
data, err := ioutil.ReadFile("./data/data.json")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProduct takes id as input and returns the corresponding product, else it returns ErrNoProduct error.
|
||||||
|
func GetProduct(id string) (Product, error) {
|
||||||
|
// Read JSON file
|
||||||
|
data, err := ioutil.ReadFile("./data/data.json")
|
||||||
|
if err != nil {
|
||||||
|
return Product{}, err
|
||||||
|
}
|
||||||
|
// read products
|
||||||
|
var products []Product
|
||||||
|
err = json.Unmarshal(data, &products)
|
||||||
|
if err != nil {
|
||||||
|
return Product{}, err
|
||||||
|
}
|
||||||
|
// iterate through product array
|
||||||
|
for i := 0; i < len(products); i++ {
|
||||||
|
// if we find one product with the given ID
|
||||||
|
if products[i].ID == id {
|
||||||
|
// return product
|
||||||
|
return products[i], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Product{}, ErrNoProduct
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteProduct takes id as input and deletes the corresponding product, else it returns ErrNoProduct error.
|
||||||
|
func DeleteProduct(id string) error {
|
||||||
|
// Read JSON file
|
||||||
|
data, err := ioutil.ReadFile("./data/data.json")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// read products
|
||||||
|
var products []Product
|
||||||
|
err = json.Unmarshal(data, &products)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// iterate through product array
|
||||||
|
for i := 0; i < len(products); i++ {
|
||||||
|
// if we find one product with the given ID
|
||||||
|
if products[i].ID == id {
|
||||||
|
products = removeElement(products, i)
|
||||||
|
// Write Updated JSON file
|
||||||
|
updatedData, err := json.Marshal(products)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile("./data/data.json", updatedData, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ErrNoProduct
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddProduct adds an input product to the product list in JSON document.
|
||||||
|
func AddProduct(product Product) error {
|
||||||
|
// Load existing products and append the data to product list
|
||||||
|
var products []Product
|
||||||
|
data, err := ioutil.ReadFile("./data/data.json")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Load our JSON file to memory using array of products
|
||||||
|
err = json.Unmarshal(data, &products)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Add new Product to our list
|
||||||
|
products = append(products, product)
|
||||||
|
|
||||||
|
// Write Updated JSON file
|
||||||
|
updatedData, err := json.Marshal(products)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile("./data/data.json", updatedData, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeElement is used to remove element from product array at given index
|
||||||
|
func removeElement(arr []Product, index int) []Product {
|
||||||
|
ret := make([]Product, 0)
|
||||||
|
ret = append(ret, arr[:index]...)
|
||||||
|
return append(ret, arr[index+1:]...)
|
||||||
|
}
|
5
go.mod
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module github.com/HelloWorld/goProductAPI
|
||||||
|
|
||||||
|
go 1.16
|
||||||
|
|
||||||
|
require github.com/gorilla/mux v1.8.0
|
2
go.sum
Normal file
2
go.sum
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
166
handlers/handlers.go
Normal file
166
handlers/handlers.go
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/subtle"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/HelloWorld/goProductAPI/entity"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetProductsHandler is used to get data inside the products defined on our product catalog
|
||||||
|
func GetProductsHandler() http.HandlerFunc {
|
||||||
|
return func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
data, err := entity.GetProducts()
|
||||||
|
if err != nil {
|
||||||
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Write the body with JSON data
|
||||||
|
rw.Header().Add("content-type", "application/json")
|
||||||
|
rw.WriteHeader(http.StatusFound)
|
||||||
|
rw.Write(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProductHandler is used to get data inside the products defined on our product catalog
|
||||||
|
func GetProductHandler() http.HandlerFunc {
|
||||||
|
return func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
// Read product ID
|
||||||
|
productID := mux.Vars(r)["id"]
|
||||||
|
product, err := entity.GetProduct(productID)
|
||||||
|
if err != nil {
|
||||||
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
responseData, err := json.Marshal(product)
|
||||||
|
if err != nil {
|
||||||
|
// Check if it is No product error or any other error
|
||||||
|
if errors.Is(err, entity.ErrNoProduct) {
|
||||||
|
// Write Header if no related product found.
|
||||||
|
rw.WriteHeader(http.StatusNoContent)
|
||||||
|
} else {
|
||||||
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Write body with found product
|
||||||
|
rw.Header().Add("content-type", "application/json")
|
||||||
|
rw.WriteHeader(http.StatusFound)
|
||||||
|
rw.Write(responseData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateProductHandler is used to create a new product and add to our product store.
|
||||||
|
func CreateProductHandler() http.HandlerFunc {
|
||||||
|
return func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
// Read incoming JSON from request body
|
||||||
|
data, err := ioutil.ReadAll(r.Body)
|
||||||
|
// If no body is associated return with StatusBadRequest
|
||||||
|
if err != nil {
|
||||||
|
rw.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Check if data is proper JSON (data validation)
|
||||||
|
var product entity.Product
|
||||||
|
err = json.Unmarshal(data, &product)
|
||||||
|
if err != nil {
|
||||||
|
rw.WriteHeader(http.StatusExpectationFailed)
|
||||||
|
rw.Write([]byte("Invalid Data Format"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = entity.AddProduct(product)
|
||||||
|
if err != nil {
|
||||||
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// return after writing Body
|
||||||
|
rw.WriteHeader(http.StatusCreated)
|
||||||
|
rw.Write([]byte("Added New Product"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteProductHandler deletes the product with given ID.
|
||||||
|
func DeleteProductHandler() http.HandlerFunc {
|
||||||
|
return func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
// Read product ID
|
||||||
|
productID := mux.Vars(r)["id"]
|
||||||
|
err := entity.DeleteProduct(productID)
|
||||||
|
if err != nil {
|
||||||
|
// Check if it is No product error or any other error
|
||||||
|
if errors.Is(err, entity.ErrNoProduct) {
|
||||||
|
// Write Header if no related product found.
|
||||||
|
rw.WriteHeader(http.StatusNoContent)
|
||||||
|
} else {
|
||||||
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Write Header with Accepted Status (done operation)
|
||||||
|
rw.WriteHeader(http.StatusAccepted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateProductHandler updates the product with given ID.
|
||||||
|
func UpdateProductHandler() http.HandlerFunc {
|
||||||
|
return func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
// Read product ID
|
||||||
|
productID := mux.Vars(r)["id"]
|
||||||
|
err := entity.DeleteProduct(productID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, entity.ErrNoProduct) {
|
||||||
|
rw.WriteHeader(http.StatusNoContent)
|
||||||
|
} else {
|
||||||
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Read incoming JSON from request body
|
||||||
|
data, err := ioutil.ReadAll(r.Body)
|
||||||
|
// If no body is associated return with StatusBadRequest
|
||||||
|
if err != nil {
|
||||||
|
rw.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Check if data is proper JSON (data validation)
|
||||||
|
var product entity.Product
|
||||||
|
err = json.Unmarshal(data, &product)
|
||||||
|
if err != nil {
|
||||||
|
rw.WriteHeader(http.StatusExpectationFailed)
|
||||||
|
rw.Write([]byte("Invalid Data Format"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Addproduct with the requested body
|
||||||
|
err = entity.AddProduct(product)
|
||||||
|
if err != nil {
|
||||||
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Write Header if no related product found.
|
||||||
|
rw.WriteHeader(http.StatusAccepted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AuthHandler(h http.Handler) http.HandlerFunc {
|
||||||
|
return func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
user, pass, ok := r.BasicAuth()
|
||||||
|
if ok {
|
||||||
|
username := sha256.Sum256([]byte(os.Getenv("USER_NAME")))
|
||||||
|
password := sha256.Sum256([]byte(os.Getenv("USER_PASS")))
|
||||||
|
userHash := sha256.Sum256([]byte(user))
|
||||||
|
passHash := sha256.Sum256([]byte(pass))
|
||||||
|
validUser := subtle.ConstantTimeCompare(userHash[:],username[:]) == 1
|
||||||
|
validPass := subtle.ConstantTimeCompare(passHash[:],password[:]) == 1
|
||||||
|
if validPass && validUser{
|
||||||
|
h.ServeHTTP(rw,r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
http.Error(rw, "No/Invalid Credentials", http.StatusUnauthorized)
|
||||||
|
}
|
||||||
|
}
|
32
main.go
Normal file
32
main.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/HelloWorld/goProductAPI/handlers"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Create new Router
|
||||||
|
router := mux.NewRouter()
|
||||||
|
// route properly to respective handlers
|
||||||
|
router.Handle("/products", handlers.GetProductsHandler()).Methods("GET")
|
||||||
|
router.Handle("/products", handlers.CreateProductHandler()).Methods("POST")
|
||||||
|
router.Handle("/products/{id}", handlers.GetProductHandler()).Methods("GET")
|
||||||
|
router.Handle("/products/{id}", handlers.DeleteProductHandler()).Methods("DELETE")
|
||||||
|
router.Handle("/products/{id}", handlers.UpdateProductHandler()).Methods("PUT")
|
||||||
|
|
||||||
|
// Create new server and assign the router
|
||||||
|
server := http.Server{
|
||||||
|
Addr: ":9090",
|
||||||
|
Handler: handlers.AuthHandler(router),
|
||||||
|
}
|
||||||
|
fmt.Println("Staring Product Catalog server on Port 9090")
|
||||||
|
// Start Server on defined port/host.
|
||||||
|
err := server.ListenAndServeTLS("server.crt", "server.key")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to start HTTPS server: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
22
server.crt
Normal file
22
server.crt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDtTCCAp2gAwIBAgIUDQgu9+HrJrc7TEelsB1TYp+zhPkwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwajELMAkGA1UEBhMCSU4xDjAMBgNVBAgMBURlbGhpMRIwEAYDVQQHDAlOZXcg
|
||||||
|
RGVsaGkxFDASBgNVBAoMC0hlbGxvIFdvcmxkMQ0wCwYDVQQLDARCbG9nMRIwEAYD
|
||||||
|
VQQDDAlsb2NhbGhvc3QwHhcNMjEwODA1MDMzNDQ0WhcNMzEwODAzMDMzNDQ0WjBq
|
||||||
|
MQswCQYDVQQGEwJJTjEOMAwGA1UECAwFRGVsaGkxEjAQBgNVBAcMCU5ldyBEZWxo
|
||||||
|
aTEUMBIGA1UECgwLSGVsbG8gV29ybGQxDTALBgNVBAsMBEJsb2cxEjAQBgNVBAMM
|
||||||
|
CWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL8emPJ1
|
||||||
|
tw0TrfJZQeLzHO2x0O36UetzeJKdf//IKt3P89B+2GGL6DzPdntTBM9J4PAzLzXe
|
||||||
|
uR8uL53XDWdfvhCd9oMYsgJ4L1XE6q+oZA6zWGC4voUwUOJgpJyXt8fdDdn1G6X/
|
||||||
|
7Lyjw4u6KOkrFcwIPaM+MxgZGwICi1JDj7sYCOEw1gWsLrgD4XpN0Fky9QH1Uue+
|
||||||
|
sxCZrrXFxiSXt0Xq51sT5BXrCkPhRrHUyhBay+VtVa+Lr2Yq+UfT3VUWERgcoJkx
|
||||||
|
YluYXMbV4YYZ53bbgrW+29UT+NKNKTF2dFJazZ4r5OzZAD3ihikvoCIZUaVtgOpf
|
||||||
|
fctWqG5bKmVpSaMCAwEAAaNTMFEwHQYDVR0OBBYEFPiCPV5yetrvDF5caB0t4XWx
|
||||||
|
S+NeMB8GA1UdIwQYMBaAFPiCPV5yetrvDF5caB0t4XWxS+NeMA8GA1UdEwEB/wQF
|
||||||
|
MAMBAf8wDQYJKoZIhvcNAQELBQADggEBALiBoaqc9KxscNuU2mXTfXD4kv1bLy3C
|
||||||
|
zzGn3bBHMQuRfwkclkEZG0Cbmu+k30DtMhUt9DREM6fVhClUlR6my/0vC/EgUIm2
|
||||||
|
yYOF/dBIySH/JkZz0qRst5NvicwPaqUQoyIIzk93Da8eE0uGHW7eV4tsAVkB0RHT
|
||||||
|
i4/EkGKZ/vJKyj/RI4zWvMCEFZcGtDtKf4rYzmcA3t0P1gbWt2+Gkhz1/+i52oIr
|
||||||
|
w2M2CQ9qpgv3jvwCfOpZVQDRMZnLVpP/s8KyOwnVu+n8c/z+zJe+U3x5Sl0eamDA
|
||||||
|
7CQ6RcorzWWzAhAQkrH0qWnF+BfF7GJ8VoLku0dnTzYsAQ5L4XaYf/I=
|
||||||
|
-----END CERTIFICATE-----
|
27
server.key
Normal file
27
server.key
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEAvx6Y8nW3DROt8llB4vMc7bHQ7fpR63N4kp1//8gq3c/z0H7Y
|
||||||
|
YYvoPM92e1MEz0ng8DMvNd65Hy4vndcNZ1++EJ32gxiyAngvVcTqr6hkDrNYYLi+
|
||||||
|
hTBQ4mCknJe3x90N2fUbpf/svKPDi7oo6SsVzAg9oz4zGBkbAgKLUkOPuxgI4TDW
|
||||||
|
BawuuAPhek3QWTL1AfVS576zEJmutcXGJJe3RernWxPkFesKQ+FGsdTKEFrL5W1V
|
||||||
|
r4uvZir5R9PdVRYRGBygmTFiW5hcxtXhhhnndtuCtb7b1RP40o0pMXZ0UlrNnivk
|
||||||
|
7NkAPeKGKS+gIhlRpW2A6l99y1aoblsqZWlJowIDAQABAoIBAQClfS0a5Ws3246H
|
||||||
|
h1pR1gl6mLo9Fr/QjRAehFrNdNoJb4PDSdK7xJW38jy51M0ZYPNxiiCbGNxbb3az
|
||||||
|
yf9FP9YoNV+7bKrXEJKMRhKhP8JEKG+icNYoJgoju2NOZOEyIutXi7IBL3Yicftl
|
||||||
|
BjFelXwuTARzUeyUNUj5mJJjDTVr3oi7A/HyoEn66PmWSGgNB618tYxA55PwRgJO
|
||||||
|
hd+OakpW90kLb3RsySts9cpIh1eEJLTKafTR4uY7SrAvMjFa9Zn4MpRYXMEq43DC
|
||||||
|
382dlqYBPXMPN4YJEKZ5CmxFMaZ3a1Nr0G60RyQqwmB/fRWg+OZUOjjFOVllnOOY
|
||||||
|
5jnSIy9BAoGBAONwsIpaze2TeM8Ij4NxBqwRAh/2zb5t0okD/zbhf2sPyrEP8+hI
|
||||||
|
egG10kQh7t0MhRklSOmv3NfDxr998XfBjswXdnsoIvP+BmlLG01zRh/49cdBnOty
|
||||||
|
Za/zu5msyYspopPrDjwF+RtMHOXpXNuzFhxMYWdfOBO7/lOSka8ABFqZAoGBANce
|
||||||
|
VzQW6OFLAE5fwwkf5W4cjOGaSpkKq6KwBkQayvRrGQMf3pu7Q2pdlcoJIhGOy8Bc
|
||||||
|
N2iI7qE4P52MmAg51TyIDcrhTrMLkoMXLmeyBC1M6JT6t1yGTpPkf6ZdVFAZZEAp
|
||||||
|
3l2dv8pyPRpr5juE8YcabgF5deo3zxieB6bYlkebAoGAS63goHjsksQCa+luT49Z
|
||||||
|
aAHU0iv+dAH5DyxsTKemDUrY6CflwgHzzwPgLlmYMKeM1jwo0dF5y7XSOT/ADFg0
|
||||||
|
msan3v0Q/F0nZvvd3tyfld3yclXr0BBls7GHV/A9s/erqEqLlv9pz2J5LyuCgXxK
|
||||||
|
vCnSM2Jkt3RTgR2BKlj4GekCgYAxUxilrfcZ6WuZjOWYiwK9W7iF5i3ip4qxU/Er
|
||||||
|
3oTYxFHI4J7XUHnlwq2c1LlGE1rusXZW9sbYmqAjjOAzSqd1KLEY6s5zyVx/yGnw
|
||||||
|
huXkSTUvK8mtYnJUANmwGMhDUX8mIzOEfa5DSixuiX0R+qqy0sGUfvgli0RmHZ4d
|
||||||
|
iJ30rwKBgDuW1L0DJnH2n8e3uhJF8W1rKDynQdzGdPc4Bl8oSfvDMpIGz1cTecKV
|
||||||
|
+30AWz1ui5u1qlg7gsjnt6Z7g3ztXfHNP1gHws5lC8YsjAEOyZ2f6JHOGAmHOsRb
|
||||||
|
G1T9WS4wq4N1DJVJ1aQcgvF1BuejzUwyWTABgpbSQzCIaUXAeX9I
|
||||||
|
-----END RSA PRIVATE KEY-----
|
Loading…
x
Reference in New Issue
Block a user