initial commit

This commit is contained in:
Tigor Hutasuhut 2024-08-22 09:18:44 +07:00
commit 10296fb4dd
16 changed files with 384 additions and 0 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
.direnv
gen

4
cmd/zen/main.go Normal file
View file

@ -0,0 +1,4 @@
package main
// This is the main entry point for CLI interface.
func main() {}

26
flake.lock Normal file
View file

@ -0,0 +1,26 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1724177844,
"narHash": "sha256-G7Mf9uN9m8FimeP3eMHu/dOC4QS8QAzo0h4ZIlDHcCA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d13fa5a45a34e7c8be33474f58003914430bdc5a",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixpkgs-unstable",
"type": "indirect"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

31
flake.nix Normal file
View file

@ -0,0 +1,31 @@
{
inputs = {
nixpkgs.url = "nixpkgs/nixpkgs-unstable";
};
outputs = { nixpkgs, ... }:
let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; };
in
{
devShell.${system} = pkgs.mkShell {
name = "zen-shell";
buildInputs = with pkgs; [
go
nodePackages_latest.nodejs
goose
air
upx
buf
buf-language-server
protoc-gen-go
protoc-gen-connect-go
protoc-gen-validate
air
gopls
];
};
};
}

3
go.mod Normal file
View file

@ -0,0 +1,3 @@
module gitlab.bareksa.com/backend/zen
go 1.22.5

View file

@ -0,0 +1,24 @@
version: v2
managed:
enabled: true
disable:
- module: buf.build/bufbuild/protovalidate
- module: buf.build/protocolbuffers/wellknowntypes
override:
- file_option: go_package_prefix
value: gitlab.bareksa.com/backend/zen/zcore/internal/gen/proto
plugins:
- remote: buf.build/protocolbuffers/go:v1.34.2
out: ../../zcore/internal/gen/proto
opt:
- paths=source_relative
- remote: buf.build/connectrpc/go
out: ../../zcore/internal/gen/proto
opt:
- paths=source_relative
- remote: buf.build/community/pseudomuto-doc:v1.5.1
out: ../../zcore/internal/gen/proto

2
schemas/proto/buf.lock Normal file
View file

@ -0,0 +1,2 @@
# Generated by buf. DO NOT EDIT.
version: v2

4
schemas/proto/buf.yaml Normal file
View file

@ -0,0 +1,4 @@
version: v2
deps:
- buf.build/bufbuild/protovalidate
- buf.build/protocolbuffers/wellknowntypes

View file

@ -0,0 +1,19 @@
syntax = "proto3";
package notify.v1;
import "notify/v1/send_notification.proto";
service NotifyService {
rpc SendNotification(SendNotificationRequest) returns (SendNotificationResponse);
rpc SendAttachment(stream SendAttachmentRequest) returns (SendAttachmentResponse);
}
message SendAttachmentRequest {
string notification_id = 1;
string name = 2;
string content_type = 3;
bytes chunk = 4;
}
message SendAttachmentResponse {}

View file

@ -0,0 +1,79 @@
syntax = "proto3";
package notify.v1;
import "notify/v1/types.proto";
message SendNotificationRequest {
Payload payload = 1;
Service service = 2;
}
// SendAttachmentResponse is sent when message is successful.
message SendNotificationResponse {
// notification_id is the snowflake id of the message.
//
// Guaranteed to be unique even on distributed environment.
//
// notification_id can be used in `SendAttachment` rpc to
// attach binary data to the message.
string notification_id = 1;
}
message Payload {
string message = 1;
Level level = 2;
optional int64 code = 3;
// details adds context to the message.
//
// like timestamps, invalid inputs,
// request payloads, backend response, etc.
//
// Anything that can enrich why this message
// appears will help.
//
// DO NOT INCLUDE BINARIES LIKE IMAGES, PDFS, DOCS, IN
// THIS PAYLOAD. USE `SendAttachment` rpc
// to attach binary values instead since they are designed
// for streaming. Server and Client RAM
// can be eaten alive if you failed to do so since GRPC
// handles via whole messages.
oneof details {
// Sends JSON as details.
bytes d_json = 4;
string d_text = 5;
}
oneof error {
bytes e_json = 6;
string e_text = 7;
}
}
message Service {
// The name of the service that is sending the notification.
//
// This is used to identify the service that is sending the notification.
//
// The value of name should be related to the product or service.
// e.g. `sbn-frontend`, `sbn-cron-job`, `payment-frontend`, `robo-frontend`.
//
// `name`, `type`, and `environment` combination are used to select which channel the
// discord notification is sent.
string name = 1;
string type = 2;
Environment environment = 3;
optional string version = 4;
// domain specifies whose message this belongs to.
//
// It's not required to be set, but setting this field
// will help zen to categorize messages when building
// reports.
ServiceDomain domain = 5;
repeated Attribute attributes = 6;
}
enum ServiceDomain {
SERVICE_DOMAIN_UNSPECIFIED = 0;
SERVICE_DOMAIN_FRONTEND = 1;
SERVICE_DOMAIN_BACKEND = 2;
}

View file

@ -0,0 +1,23 @@
syntax = "proto3";
package notify.v1;
enum Level {
LEVEL_UNSPECIFIED = 0;
LEVEL_DEBUG = 1;
LEVEL_INFO = 2;
LEVEL_WARN = 3;
LEVEL_ERROR = 4;
}
enum Environment {
ENVIRONMENT_UNSPECIFIED = 0;
ENVIRONMENT_DEVELOPMENT = 1;
ENVIRONMENT_STAGING = 2;
ENVIRONMENT_PRODUCTION = 3;
}
message Attribute {
string key = 1;
string value = 2;
}

View file

@ -0,0 +1 @@
package server

33
zcore/zerr/zerr.go Normal file
View file

@ -0,0 +1,33 @@
package zerr
import (
"context"
"time"
"gitlab.bareksa.com/backend/zen/zcore/zoptions"
)
type Error interface {
error
Code(code int) Error
GetCode() int
Message(msg string, args ...any) Error
GetMessage() string
PublicMessage(msg string, args ...any) Error
GetPublicMessage() string
Caller(pc uintptr) Error
GetCaller() uintptr
Time(t time.Time) Error
GetTime() time.Time
Key(msg string, args ...any) Error
GetKey() string
Log(ctx context.Context) Error
Notify(ctx context.Context, opts ...zoptions.NotifyOption) Error
}

1
zcore/zlog/zlog.go Normal file
View file

@ -0,0 +1 @@
package zlog

131
zcore/zoptions/notify.go Normal file
View file

@ -0,0 +1,131 @@
package zoptions
import "time"
type NotifyParameters struct {
// AlwaysSend makes sure this message
// is always sent no matter the condition.
//
// It will cause `zen` to ignore every other
// parameters and just send the message.
AlwaysSend bool
// InitialBackoff is the starting backoff.
//
// If not specified, the default duration is time.Minute.
InitialBackoff time.Duration
// MaxBackoff is the maximum backoff. If unset, the default value
// is time.Hour * 24.
MaxBackoff time.Duration
// BackoffFormula is a string that represents the formula to calculate the backoff time
// when multiple message with the same key is fired off repeatedly.
//
// It uses CEL (Common Expression Language) syntax. See: https://github.com/google/cel-go
// for the language spec.
//
// Expression must return a duration type.
//
// If invalid cel expression, empty string, or wrong return value, default formula will be used.
//
// Available variables:
//
// - `repeat`: the number of times the notification has been sent. type: int.
// - `last`: the last time the notification was sent. type: timestamp.
// - `prev_backoff`: the backoff value from previous evaluation. 0 if first seen. Type: duration.
// - `initial_backoff`: the initial_backoff value. Type: duration
// - `max_backoff`: the maximum backoff value. Type: duration
//
// Available Non-Standard functions:
//
// // pow multiplies the base with the exponent
// pow(base: double, exp: double) -> double
//
// // mult_dur_double multiplies the duration with the double value
// mult_dur_double(dur: duration, mult: double) -> duration
//
//
// Example (and also default formula):
//
// mult_dur_double(initial_backoff, pow(1.5, double(repeat))) > max_backoff
// ? max_backoff
// : mult_dur_double(initial_backoff, pow(1.5, double(repeat)))
BackoffFormula string
}
type NotifyOption interface {
Apply(parameters *NotifyParameters)
}
type NotifyOptionFunc func(parameters *NotifyParameters)
func (f NotifyOptionFunc) Apply(parameters *NotifyParameters) {
f(parameters)
}
type NotifiyOptionBuilder []NotifyOption
func (no NotifiyOptionBuilder) Apply(parameters *NotifyParameters) {
for _, opt := range no {
opt.Apply(parameters)
}
}
func (no NotifiyOptionBuilder) AlwaysSend(alwaysSend bool) NotifiyOptionBuilder {
return append(no, NotifyOptionFunc(func(parameters *NotifyParameters) {
parameters.AlwaysSend = alwaysSend
}))
}
func (no NotifiyOptionBuilder) InitialBackoff(backoff time.Duration) NotifiyOptionBuilder {
return append(no, NotifyOptionFunc(func(parameters *NotifyParameters) {
parameters.InitialBackoff = backoff
}))
}
func (no NotifiyOptionBuilder) MaxBackoff(maxBackoff time.Duration) NotifiyOptionBuilder {
return append(no, NotifyOptionFunc(func(parameters *NotifyParameters) {
parameters.MaxBackoff = maxBackoff
}))
}
// BackoffFormula is a string that represents the formula to calculate the backoff time
// when multiple message with the same key is fired off repeatedly.
//
// It uses CEL (Common Expression Language) syntax. See: https://github.com/google/cel-go
// for the language spec.
//
// Expression must return a duration type.
//
// If invalid cel expression, empty string, or wrong return value, default formula will be used.
//
// Available variables:
//
// - `repeat`: the number of times the notification has been sent. type: int.
// - `last`: the last time the notification was sent. type: timestamp.
// - `prev_backoff`: the backoff value from previous evaluation. 0 if this message is first seen. Type: duration.
// - `initial_backoff`: the initial_backoff value. Type: duration
// - `max_backoff`: the maximum backoff value. Type: duration
//
// Available Non-Standard functions:
//
// // pow multiplies the base with the exponent
// pow(base: double, exp: double) -> double
//
// // mult_dur_double multiplies the duration with the double value
// mult_dur_double(dur: duration, mult: double) -> duration
//
// Example (and also default formula):
//
// mult_dur_double(initial_backoff, pow(1.5, double(repeat))) > max_backoff
// ? max_backoff
// : mult_dur_double(initial_backoff, pow(1.5, double(repeat)))
func (no NotifiyOptionBuilder) BackoffFormula(formula string) NotifiyOptionBuilder {
return append(no, NotifyOptionFunc(func(parameters *NotifyParameters) {
parameters.BackoffFormula = formula
}))
}
func Notify() NotifiyOptionBuilder {
return NotifiyOptionBuilder{}
}