Skip to content
Merged
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
2 changes: 0 additions & 2 deletions .eslintignore

This file was deleted.

25 changes: 0 additions & 25 deletions .eslintrc.json

This file was deleted.

37 changes: 37 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Go Build

on:
push:
branches: [ main ]
pull_request:


jobs:
build:
strategy:
matrix:
os: [ubuntu-latest]
go-version: [1.24]
runs-on: ${{ matrix.os }}

steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go-version }}

- name: Cache Go modules
uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-${{ matrix.go-version }}-

- name: Build project
run: go build -v cmd/main.go
43 changes: 30 additions & 13 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,37 @@
name: Hello World
name: Go Tests

on:
push:
branches:
- main
branches: [ main ]
pull_request:
branches:
- main
workflow_dispatch:


jobs:
hello:
runs-on: ubuntu-latest
test:
strategy:
matrix:
os: [ubuntu-latest]
go-version: [1.24]
runs-on: ${{ matrix.os }}

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Print Hello World
run: echo "Hello, World!"
- name: Checkout code
uses: actions/checkout@v3

- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go-version }}

- name: Cache Go modules
uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-${{ matrix.go-version }}-

- name: Run tests recursively
run: go test ./...
78 changes: 25 additions & 53 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,53 +1,25 @@
FROM node:16

RUN mkdir -p /usr/src/app && \
chown node:node /usr/src/app

USER node:node

WORKDIR /usr/src/app

COPY --chown=node:node package*.json .

RUN npm ci

COPY --chown=node:node . .

ENV LOGGING_LEVEL=verbose \
LOGGING_TYPE=Console \
LOGGING_COLORIZE=true

ENV HOST=0.0.0.0\
PORT=7777\
KEY_LENGTH=10\
MAX_LENGTH=10000000\
STATIC_MAX_AGE=3

ENV KEYGENERATOR_TYPE=phonetic \
KEYGENERATOR_KEYSPACE=""

ENV RATELIMITS_NORMAL_TOTAL_REQUESTS=500\
RATELIMITS_NORMAL_EVERY_MILLISECONDS=60000 \
RATELIMITS_WHITELIST_TOTAL_REQUESTS="" \
RATELIMITS_WHITELIST_EVERY_MILLISECONDS="" \
# comma separated list for the whitelisted \
RATELIMITS_WHITELIST=example1.whitelist,example2.whitelist \
\
RATELIMITS_BLACKLIST_TOTAL_REQUESTS="" \
RATELIMITS_BLACKLIST_EVERY_MILLISECONDS="" \
# comma separated list for the blacklisted \
RATELIMITS_BLACKLIST=example1.blacklist,example2.blacklist
ENV DOCUMENTS="about=./about.md"

EXPOSE ${PORT}
STOPSIGNAL SIGINT
ENTRYPOINT [ "bash", "docker-entrypoint.sh" ]

HEALTHCHECK --interval=30s --timeout=30s --start-period=5s \
--retries=3 CMD [ "sh", "-c", "echo -n 'curl localhost:7777... '; \
(\
curl -sf localhost:7777 > /dev/null\
) && echo OK || (\
echo Fail && exit 2\
)"]
CMD ["npm", "start"]
FROM golang:1.24-alpine AS builder

WORKDIR /src

# Copy go mod and project files
COPY go.* ./
RUN go mod download
COPY . .

# Install necessary dependencies
RUN apk update && apk add --no-cache git ca-certificates openssl && update-ca-certificates

# Build the binary
RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o ./hastebin ./cmd/main.go

FROM gcr.io/distroless/static:nonroot
COPY --from=builder /etc/ssl/certs /etc/ssl/certs

# Copy the binary from the builder stage
COPY --from=builder /src/hastebin /bin/hastebin

# Set the user and group
USER 0:0

CMD ["/bin/hastebin", "--config", "/app/config.yaml"]
1 change: 0 additions & 1 deletion Procfile

This file was deleted.

110 changes: 110 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package main

import (
"context"
"flag"
"io"
"os"
"os/signal"
"syscall"
"time"

"github.com/armbian/ansi-hastebin/config"
"github.com/armbian/ansi-hastebin/internal/keygenerator"
"github.com/armbian/ansi-hastebin/internal/server"
"github.com/armbian/ansi-hastebin/internal/storage"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)

func handleConfig(location string) (*config.Config, storage.Storage, keygenerator.KeyGenerator) {
cfg := config.NewConfig(location)
exp := time.Duration(cfg.Expiration)

var pasteStorage storage.Storage
switch cfg.Storage.Type {
case "file":
pasteStorage = storage.NewFileStorage(cfg.Storage.FilePath, exp)
case "redis":
pasteStorage = storage.NewRedisStorage(cfg.Storage.Host, cfg.Storage.Port, cfg.Storage.Username, cfg.Storage.Password, exp)
case "memcached":
pasteStorage = storage.NewMemcachedStorage(cfg.Storage.Host, cfg.Storage.Port, int(cfg.Expiration))
case "mongodb":
pasteStorage = storage.NewMongoDBStorage(cfg.Storage.Host, cfg.Storage.Port, cfg.Storage.Username, cfg.Storage.Password, cfg.Storage.Database, exp)
case "postgres":
pasteStorage = storage.NewPostgresStorage(cfg.Storage.Host, cfg.Storage.Port, cfg.Storage.Username, cfg.Storage.Password, cfg.Storage.Database, int(cfg.Expiration))
case "s3":
pasteStorage = storage.NewS3Storage(cfg.Storage.Host, cfg.Storage.Port, cfg.Storage.Username, cfg.Storage.Password, cfg.Storage.AWSRegion, cfg.Storage.Bucket)
default:
log.Fatal().Str("storage_type", cfg.Storage.Type).Msg("Unknown storage type")
return nil, nil, nil
}

// Set static documents from config
for _, doc := range cfg.Documents {
file, err := os.OpenFile(doc.Path, os.O_RDONLY, 0644)
if err != nil {
log.Fatal().Err(err).Str("path", doc.Path).Msg("Failed to open document")
}

content, err := io.ReadAll(file)
if err != nil {
log.Fatal().Err(err).Str("path", doc.Path).Msg("Failed to read document")
}
file.Close()

if err := pasteStorage.Set(doc.Key, string(content), false); err != nil {
log.Fatal().Err(err).Str("key", doc.Key).Msg("Failed to set document")
}
}

var keyGenerator keygenerator.KeyGenerator

switch cfg.KeyGenerator {
case "random":
keyGenerator = keygenerator.NewRandomKeyGenerator(cfg.KeySpace)
case "phonetic":
keyGenerator = keygenerator.NewPhoneticKeyGenerator()
default:
log.Fatal().Str("key_generator", cfg.KeyGenerator).Msg("Unknown key generator")
return nil, nil, nil
}

// Adjust logger
logLevel, err := zerolog.ParseLevel(cfg.Logging.Level)
if err != nil {
log.Fatal().Err(err).Str("level", cfg.Logging.Level).Msg("Failed to parse log level")
}
log.Logger = log.Level(logLevel)

if cfg.Logging.Colorize {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout})
}

return cfg, pasteStorage, keyGenerator
}

func main() {
// Parse command line arguments
var configFile string
flag.StringVar(&configFile, "config", "config.yaml", "Configuration file")
flag.Parse()

srv := server.NewServer(handleConfig(configFile))
srv.RegisterRoutes()

// Start the server in a separate goroutine
go func() {
srv.Start()
}()

// Wait for signal to stop the server
stopCh := make(chan os.Signal, 1)
signal.Notify(stopCh, syscall.SIGTERM, syscall.SIGINT)
<-stopCh

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

srv.Shutdown(ctx)
}
32 changes: 0 additions & 32 deletions config.json

This file was deleted.

26 changes: 26 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
host: "0.0.0.0"
port: 7777
key_length: 10
max_length: 4000000
static_max_age: 3
expiration: 0
recompress_static_assets: false
key_generator: "phonetic"

storage:
type: "file"
file_path: "./test"

documents:
- key: "about"
path: "/app/about.md"

rate_limiting:
enable: true
limit: 500
window: 15

logging:
level: "info"
type: "text"
colorize: true
Loading
Loading