Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Certificates service
===

### Запуск сервиса:
*"cmd/rpc/main.go"*

### Перед запуском необходимо:
Задать переменные окружения - **"TEMPLATES_DIR"**, **"CERTIFICATES_DIR"**, с путями хранения файлов шаблонов и сертификатов.
В системе должна быть установлена **"wkhtmltopdf"**, программа конвертации **HTML** файлов в **PDF**: https://wkhtmltopdf.org/downloads.html
Исполняемый файл **"wkhtmltopdf"** должен находиться в каталоге запуска сервиса, или быть доступным по путям из переменной окружения **"PATH"**.
Сгенерировать из **".proto"** вспомогательные файлы **gRPC**: *"protoc @protoc_options_generate.txt"*

### Требования к шаблонам:
В шаблоне могут содержаться следующие теги замены:
```
{{.CourseName}}
{{.CourseType}}
{{.CourseHours}}
{{.CourseDate}}
{{.CourseMentors}}
{{.StudentFirstname}}
{{.StudentLastname}}
{{.QrCodeLink}}
```
Шаблоны с любыми другими тегами замены будут отклонены валидатором.
Вместо тега замены `{{.QrCodeLink}}` будет вставлен **QR** код в формате **PNG**: ссылка на сертификат.
Например: `"<img src={{.QrCodeLink}} width="128" height="128">"`

### Пример простого HTML шаблона:
```
<html><body><h1 style="color:red;">Test html color<h1><p>{{.CourseName}}</p><p>{{.CourseType}}</p><p>{{.CourseHours}}</p><p>{{.CourseDate}}</p><p>{{.CourseMentors}}</p><p>{{.StudentFirstname}}</p><p>{{.StudentLastname}}</p><p><img src={{.QrCodeLink}} width="128" height="128"></p></body></html>
```
2 changes: 0 additions & 2 deletions api/proto/README.md

This file was deleted.

75 changes: 75 additions & 0 deletions api/proto/certificate.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
syntax = "proto3";

package certificate;

option go_package = "gus_certificates/protobuf/transport/certificate;certificate";

import "google/protobuf/empty.proto";

service Certificate {
rpc IssueCertificate(IssueCertificateReq) returns (IssueCertificateResp);
rpc GetCertificateFileByID(GetCertificateFileByIDReq) returns (GetCertificateFileByIDResp);
rpc GetCertificateLinkByID(GetCertificateLinkByIDReq) returns (GetCertificateLinkByIDResp);
rpc AddTemplate(AddTemplateReq) returns (AddTemplateResp);
rpc DelTemplate(DelTemplateReq) returns (DelTemplateResp);
}

message IssueCertificateReq {
StudentMessage student = 1;
string template_name = 2;
CourseMessage course = 3;
}

message IssueCertificateResp {
string id = 1;
}

message GetCertificateFileByIDReq {
string id = 1;
}

message GetCertificateLinkByIDReq {
string id = 1;
}

message GetCertificateFileByIDResp {
bytes certificate = 1;
}

message GetCertificateLinkByIDResp {
string link = 1;
}

message AddTemplateReq {
string template_name = 1;
bytes template = 2;
}

message DelTemplateReq {
string template_name = 1;
}

message AddTemplateResp {
Status status = 1;
}

message DelTemplateResp {
Status status = 1;
}

message StudentMessage {
string firstname = 1;
string lastname = 2;
}

message CourseMessage {
string course_name = 1;
string course_type = 2;
string hours = 3;
string date = 4;
repeated string mentors = 5;
}

message Status {
int32 code = 1;
}
47 changes: 43 additions & 4 deletions app/certgenerator/certgenerator.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,18 @@ package certgenerator

import (
"bytes"
"crypto/md5"
"encoding/base64"
"fmt"
"html/template"
"strings"

valid "github.com/go-ozzo/ozzo-validation/v4"
)

var shortTextFieldRule = []valid.Rule{valid.Required, valid.RuneLength(1, 50)}
var longTextFieldRule = []valid.Rule{valid.Required, valid.RuneLength(1, 250)}

type CertGenerator struct {
data certData
}
Expand All @@ -18,6 +26,19 @@ type certData struct {
CourseMentors string
StudentFirstname string
StudentLastname string
QrCodeLink template.URL
}

func (c *CertGenerator) ValidateData() error {
return valid.ValidateStruct(&c.data,
valid.Field(&c.data.CourseName, longTextFieldRule...),
valid.Field(&c.data.CourseType, shortTextFieldRule...),
valid.Field(&c.data.CourseHours, shortTextFieldRule...),
valid.Field(&c.data.CourseDate, shortTextFieldRule...),
valid.Field(&c.data.CourseMentors, longTextFieldRule...),
valid.Field(&c.data.StudentFirstname, shortTextFieldRule...),
valid.Field(&c.data.StudentLastname, shortTextFieldRule...),
)
}

func (c *CertGenerator) GenerateCertHTML(templateHTMLData []byte) ([]byte, error) {
Expand All @@ -38,10 +59,21 @@ func (c *CertGenerator) GenerateCertHTML(templateHTMLData []byte) ([]byte, error
return buf.Bytes(), nil
}

// func (c *CertGenerator) getDataForIDGenerator() string {
// return fmt.Sprintf("%s%s%s%s%s%s", c.data.CourseName, c.data.CourseType, c.data.CourseHours,
// c.data.CourseDate, c.data.StudentFirstname, c.data.StudentLastname)
// }
func (c *CertGenerator) CheckTemplateHTML(templateHTMLData []byte) error {
_, err := c.GenerateCertHTML(templateHTMLData)

return err
}

func (c *CertGenerator) GenerateID() string {
data := []byte(c.getDataForIDGenerator())
return fmt.Sprintf("%x", md5.Sum(data))
}

func (c *CertGenerator) getDataForIDGenerator() string {
return fmt.Sprintf("%s%s%s%s%s%s", c.data.CourseName, c.data.CourseType, c.data.CourseHours,
c.data.CourseDate, c.data.StudentFirstname, c.data.StudentLastname)
}

func (c *CertGenerator) SetCourseName(courseName string) {
c.data.CourseName = courseName
Expand Down Expand Up @@ -70,3 +102,10 @@ func (c *CertGenerator) SetStudentFirstname(studentFirstname string) {
func (c *CertGenerator) SetStudentLastname(studentLastname string) {
c.data.StudentLastname = studentLastname
}

func (c *CertGenerator) SetQrCodeLink(imgData []byte) {
htmlTagImagePngBase64 := "data:image/png;base64,"
imgBase64String := base64.StdEncoding.EncodeToString(imgData)

c.data.QrCodeLink = template.URL(fmt.Sprintf("%s%s", htmlTagImagePngBase64, imgBase64String))
}
37 changes: 32 additions & 5 deletions app/certgenerator/certgenerator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,24 @@ import (
)

var testData = struct {
certGenerator CertGenerator
certGenerator *CertGenerator
goodTemplate []byte
failTemplate []byte
expected []byte
expectedCert []byte
expectedId string
}{
certGenerator: CertGenerator{},
certGenerator: &CertGenerator{},
goodTemplate: []byte(`<html><body><h1 style="color:red;">Test html color<h1>
<p>{{.CourseName}}</p><p>{{.CourseType}}</p><p>{{.CourseHours}}</p><p>{{.CourseDate}}</p>
<p>{{.CourseMentors}}</p><p>{{.StudentFirstname}}</p><p>{{.StudentLastname}}</p>
</body></html>`),
failTemplate: []byte(`<html><body><h1 style="color:red;">Test html color<h1>
<p>{{.CourseName_Fail}}</p></body></html>`),
expected: []byte(`<html><body><h1 style="color:red;">Test html color<h1>
expectedCert: []byte(`<html><body><h1 style="color:red;">Test html color<h1>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Что-то не обратил внимания раньше. Тут по-хорошему должен быть failObject

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

тут не понял. Почему должен failObject? Наоборот expectedCert это образец сертификата который должен сгенерироваться.

<p>Golang</p><p>Theory</p><p>35</p><p>25.01.2023</p>
<p>Pavel Gordiyanov, Mikita Viarbovikau, Sergey Shtripling</p><p>Ivan</p><p>Ivanov</p>
</body></html>`),
expectedId: "612364afe471b3b1cc80083183fd381d",
}

func TestMain(m *testing.M) {
Expand Down Expand Up @@ -52,7 +54,32 @@ func TestGenerateCertificate(t *testing.T) {
t.Error(err)
}

if !reflect.DeepEqual(gotCertif, testData.expected) {
if !reflect.DeepEqual(gotCertif, testData.expectedCert) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Опять же - лучше использовать какой-либо testify. Но это нужно было сделать раньше, сейчас можно отдельную таску.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

переделать все тесты под testify?

t.Errorf("%q and %q should be equal", "gotSertif", "expectedCert")
}
}

func TestGenerateID(t *testing.T) {
generator := testData.certGenerator

actualId := generator.GenerateID()
if actualId != testData.expectedId {
t.Errorf("expected:%q, actual:%q", testData.expectedId, actualId)
}
}

func TestCheckTemplateHTML_fail(t *testing.T) {
generator := testData.certGenerator
err := generator.CheckTemplateHTML(testData.failTemplate)
if err == nil {
t.Error("err must not be nil")
}
}

func TestCheckTemplateHTML(t *testing.T) {
generator := testData.certGenerator
err := generator.CheckTemplateHTML(testData.goodTemplate)
if err != nil {
t.Error(err)
}
}
2 changes: 0 additions & 2 deletions app/idgenerator/README.md

This file was deleted.

2 changes: 0 additions & 2 deletions app/server/README.md

This file was deleted.

Loading