Skip to content

Commit

Permalink
Merge pull request #77 from AlertFlow/release/v1.0.0-beta14
Browse files Browse the repository at this point in the history
Release Version 1.0.0-beta14
  • Loading branch information
JustNZ authored Jan 15, 2025
2 parents 160b652 + 19760fc commit c612451
Show file tree
Hide file tree
Showing 27 changed files with 771 additions and 105 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ name: Build and Release

on:
push:
branches:
- main
tags:
- 'v*.*.*'

Expand Down
94 changes: 71 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,77 @@ AlertFlow is a monitoring automation platform designed to streamline workflows,
## Table of Contents

- [Features](#features)
- [Installation](#installation)
- [Usage](#usage)
- [Preview](#preview)
- [Self Hosting](#self-hosting)
- [Runners](#runners)
- [Project Structure](#project-structure)
- [Local Development](#local-development)
- [Contributing](#contributing)
- [License](#license)

## Features

- **Project Management**: Create, edit, and delete projects.
- **Flows**: Create flows to design workflows per alarm pattern.
- **Payloads**: Receive incoming alarms from your monitoring infrastructure via payloads & flows.
- **Runners**: Manage self-hosted runners for executing tasks.
- **Project Management**: Projects combine a number of Flows and add the option to invite members and control their access.
- **Flows**: Create flows to design workflows for your incoming alarms.
- **Payloads**: Receive incoming alarms from your monitoring infrastructure via payloads and automate them via Flows.
- **Runners**: Runners execute your workflows and can also be self-hosted.
- **Shared Runners**: Create runners which can be used for all projects.
- **Scalable to your Needs**: Runners can be scaled with the Auto Join option
- **Team Collaboration**: Invite team members, assign roles, and manage permissions.
- **Audit Logs**: Track changes and activities within projects.
- **Audit Logs**: Track changes and activities within projects and flows.

## Installation
## Preview
If you want to checkout AlertFlow before hosting it on your own, have a look at
https://alertflow.org

Login Details:
```
Username: Demo
Password: demo123
```

## Self Hosting
To run your own version of AlertFlow we provide various docker images available at
[Docker Hub](https://hub.docker.com/repository/docker/justnz/alertflow/general).
- **justnz/alertflow:latest** - Full version including frontend and backend
- **justnz/alertflow:vx.x.x** - Versioned release. Also available for the single frontend and backend images
- **justnz/alertflow:frontend-latest** - Only frontend
- **justnz/alertflow:backend-latest** - Only backend

### Full Version

Config example: [config.yaml](https://github.com/AlertFlow/alertflow/blob/main/services/backend/config/config.yaml)

```sh
docker run -p 80:3000 -v /your/config/path/config.yaml:/etc/alertflow/backend_config.yaml justnz/alertflow:latest
```

### Frontend Only
If you want to run only the frontend of AlertFlow, please provide the backend endpoint via the below env flag.
```sh
docker run -p 80:3000 -e NEXT_PUBLIC_API_URL=https://api-url.com justnz/alertflow:frontend-latest
```

### Backend Only
```sh
docker run -p 8080:8080 -v /your/config/path/config.yaml:/etc/alertflow/backend_config.yaml justnz/alertflow:backend-latest
```

## Runners
Runners are the key part of AlertFlow and provide the functionality of execution so called Workflows.

To create / run your own runners you require to have a fully set up AlertFlow instance.

Please see the repo [Runner](https://github.com/AlertFlow/runner) for more informations.

## Project Structure

The project structure is organized as follows:

- **backend**: Contains the backend code for handling API requests, database interactions, and business logic.
- **frontend**: Contains the frontend code for the user interface, including components, pages, and styles.

## Local Development

To get started with the AlertFlow project, follow these steps:

Expand All @@ -37,7 +92,7 @@ To get started with the AlertFlow project, follow these steps:
cd services/backend && go mod download
```

3. Create a [config.yaml](https://github.com/AlertFlow/alertflow/blob/main/services/backend/config/config.yaml) file in the `config` directory and add the necessary configuration:
3. Create a [config.yaml](https://github.com/AlertFlow/alertflow/blob/main/services/backend/config/config.yaml) file and add the necessary configuration:
```yaml
LogLevel: info
Expand All @@ -48,14 +103,18 @@ To get started with the AlertFlow project, follow these steps:
User: postgres
Password: postgres
Encryption:
Enabled: true
Key: your-encryption-key
JWT:
Secret: your-jwt-secret
```

4. Build and run the backend server:
```sh
go build -o alertflow-backend
./alertflow-backend --config config/config.yaml
$ go build -o alertflow-backend
$ ./alertflow-backend --config config/config.yaml
```

### Frontend
Expand All @@ -80,17 +139,6 @@ To get started with the AlertFlow project, follow these steps:
npm run dev
```

## Usage

Once the development server is running, you can access the application at `http://localhost:3000`. From there, you can create and manage projects, invite team members, and monitor project activities.

## Project Structure

The project structure is organized as follows:

- **backend**: Contains the backend code for handling API requests, database interactions, and business logic.
- **frontend**: Contains the frontend code for the user interface, including components, pages, and styles.

## Contributing

We welcome contributions to the AlertFlow project! To contribute, follow these steps:
Expand All @@ -112,4 +160,4 @@ We welcome contributions to the AlertFlow project! To contribute, follow these s

## License

This project is licensed under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3. See the [LICENSE](https://github.com/AlertFlow/alertflow/blob/main/LICENSE) file for details.
This project is licensed under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3. See the [LICENSE](https://github.com/AlertFlow/alertflow/blob/main/LICENSE) file for details.
93 changes: 93 additions & 0 deletions services/backend/database/migrations/3_encrypted_flows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package migrations

import (
"context"
"fmt"

log "github.com/sirupsen/logrus"
"github.com/uptrace/bun"
)

func init() {
Migrations.MustRegister(func(ctx context.Context, db *bun.DB) error {
return addEncryptPayloadsToFlows(ctx, db)
}, func(ctx context.Context, db *bun.DB) error {
return removeEncryptPayloadsFromFlows(ctx, db)
})
}

func addEncryptPayloadsToFlows(ctx context.Context, db *bun.DB) error {
exists, err := columnExists(ctx, db, "flows", "encrypt_payloads")
if err != nil {
return fmt.Errorf("failed to check if encrypt_payloads column exists: %v", err)
}
if !exists {
_, err := db.NewAddColumn().
Table("flows").
ColumnExpr("encrypt_payloads BOOL DEFAULT true").
Exec(ctx)

if err != nil {
return fmt.Errorf("failed to add encrypt_payloads column to flows table: %v", err)
}
} else {
log.Debug("encrypt_payloads column already exists in flows table")
}

exists, err = columnExists(ctx, db, "flows", "encrypt_executions")
if err != nil {
return fmt.Errorf("failed to check if encrypt_executions column exists: %v", err)
}
if !exists {
_, err := db.NewAddColumn().
Table("flows").
ColumnExpr("encrypt_executions BOOL DEFAULT true").
Exec(ctx)

if err != nil {
return fmt.Errorf("failed to add encrypt_executions column to flows table: %v", err)
}
} else {
log.Debug("encrypt_executions column already exists in flows table")
}

return nil
}

func removeEncryptPayloadsFromFlows(ctx context.Context, db *bun.DB) error {
exists, err := columnExists(ctx, db, "flows", "encrypt_payloads")
if err != nil {
return fmt.Errorf("failed to check if encrypt_payloads column exists: %v", err)
}
if exists {
_, err := db.NewDropColumn().
Table("flows").
Column("encrypt_payloads").
Exec(ctx)

if err != nil {
return fmt.Errorf("failed to remove encrypt_payloads column from flows table: %v", err)
}
} else {
log.Debug("encrypt_payloads column already removed from flows table")
}

exists, err = columnExists(ctx, db, "flows", "encrypt_executions")
if err != nil {
return fmt.Errorf("failed to check if encrypt_executions column exists: %v", err)
}
if exists {
_, err := db.NewDropColumn().
Table("flows").
Column("encrypt_executions").
Exec(ctx)

if err != nil {
return fmt.Errorf("failed to remove encrypt_executions column from flows table: %v", err)
}
} else {
log.Debug("encrypt_executions column already removed from flows table")
}

return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package migrations

import (
"context"
"fmt"

log "github.com/sirupsen/logrus"
"github.com/uptrace/bun"
)

func init() {
Migrations.MustRegister(func(ctx context.Context, db *bun.DB) error {
return addEncryptedToFlowComponents(ctx, db)
}, func(ctx context.Context, db *bun.DB) error {
return removeEncryptedFromFlowComponents(ctx, db)
})
}

func addEncryptedToFlowComponents(ctx context.Context, db *bun.DB) error {
exists, err := columnExists(ctx, db, "payloads", "encrypted")
if err != nil {
return fmt.Errorf("failed to check if encrypted column exists: %v", err)
}
if !exists {
_, err := db.NewAddColumn().
Table("payloads").
ColumnExpr("encrypted BOOL DEFAULT false").
Exec(ctx)

if err != nil {
return fmt.Errorf("failed to add encrypted column to payloads table: %v", err)
}
} else {
log.Debug("encrypted column already exists in payloads table")
}

exists, err = columnExists(ctx, db, "execution_steps", "encrypted")
if err != nil {
return fmt.Errorf("failed to check if encrypted column exists: %v", err)
}
if !exists {
_, err := db.NewAddColumn().
Table("execution_steps").
ColumnExpr("encrypted BOOL DEFAULT false").
Exec(ctx)

if err != nil {
return fmt.Errorf("failed to add encrypted column to execution_steps table: %v", err)
}
} else {
log.Debug("encrypted column already exists in execution_steps table")
}

return nil
}

func removeEncryptedFromFlowComponents(ctx context.Context, db *bun.DB) error {
exists, err := columnExists(ctx, db, "payloads", "encrypted")
if err != nil {
return fmt.Errorf("failed to check if encrypted column exists: %v", err)
}
if exists {
_, err := db.NewDropColumn().
Table("payloads").
Column("encrypted").
Exec(ctx)

if err != nil {
return fmt.Errorf("failed to remove encrypted column from payloads table: %v", err)
}
} else {
log.Debug("encrypted column already removed from payloads table")
}

exists, err = columnExists(ctx, db, "execution_steps", "encrypted")
if err != nil {
return fmt.Errorf("failed to check if encrypted column exists: %v", err)
}
if exists {
_, err := db.NewDropColumn().
Table("execution_steps").
Column("encrypted").
Exec(ctx)

if err != nil {
return fmt.Errorf("failed to remove encrypted column from execution_steps table: %v", err)
}
} else {
log.Debug("encrypted column already removed from execution_steps table")
}

return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package encryption

import (
"alertflow-backend/config"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"io"
)

func EncryptExecutionStepActionMessage(messages []string) ([]string, error) {
block, err := aes.NewCipher([]byte(config.Config.Encryption.Key))
if err != nil {
return nil, err
}

for i := range messages {
plaintext := []byte(messages[i])
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize]

if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}

stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)

// Encode the ciphertext as base64 to ensure it can be stored as JSON
encodedCiphertext := base64.StdEncoding.EncodeToString(ciphertext)
messages[i] = encodedCiphertext
}

return messages, nil
}

func DecryptExecutionStepActionMessage(encryptedMessage []string) ([]string, error) {
block, err := aes.NewCipher([]byte(config.Config.Encryption.Key))
if err != nil {
return nil, err
}

for i := range encryptedMessage {
encodedCiphertext := encryptedMessage[i]
ciphertext, err := base64.StdEncoding.DecodeString(encodedCiphertext)
if err != nil {
return nil, err
}

iv := ciphertext[:aes.BlockSize]
ciphertext = ciphertext[aes.BlockSize:]

stream := cipher.NewCFBDecrypter(block, iv)
stream.XORKeyStream(ciphertext, ciphertext)

encryptedMessage[i] = string(ciphertext)
}

return encryptedMessage, nil
}
Loading

0 comments on commit c612451

Please sign in to comment.