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
52 changes: 52 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: CI

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: gofmt
run: |
unformatted=$(gofmt -l .)
if [ -n "$unformatted" ]; then
echo "These files need gofmt:"
echo "$unformatted"
exit 1
fi

- name: go vet
run: go vet ./...

- name: staticcheck
run: go run honnef.co/go/tools/cmd/staticcheck@latest ./...

- name: go test
run: go test -race ./...

govulncheck:
runs-on: ubuntu-latest
# Reports known vulnerabilities in dependencies. Kept separate and
# non-blocking so a newly published advisory does not block unrelated PRs.
continue-on-error: true
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: govulncheck
run: go run golang.org/x/vuln/cmd/govulncheck@latest ./...
67 changes: 28 additions & 39 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,39 +1,28 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
._*
# Test binary, built with `go test -c`
*.test
sitemap.xml
# Output of the go coverage tool, specifically when used with LiteIDE
*.out


# Dependency directories (remove the comment below to include it)
# vendor/

# Go workspace file
go.work
config/config.yaml
go.mod
go.sum
data/database.sqlite
data/database.sqlite
static/uploads/b8hZ3RHiZ5login_EpxxSB0MXw.png
static/sitemap.xml
data/database.sqlite
static/sitemap.xml
static/uploads/*
/tmp/*

.vscode/settings.json
static/sitemap.xml
main
.DS_Store
static/sitemap.xml
# Binaries
*.exe
*.exe~
*.dll
*.so
*.dylib
/main
/goxcms
/tmp/
build-errors.log

# Test binaries and coverage
*.test
*.out

# Go workspace file
go.work

# Local config, data and generated files
config/config.yaml
data/*.sqlite
static/uploads/*
static/sitemap.xml

# Editor / OS
.vscode/settings.json
.DS_Store
._*
18 changes: 18 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.PHONY: run build test lint fmt

run:
go run .

build:
go build -o goxcms .

test:
go test ./...

lint:
test -z "$$(gofmt -l .)"
go vet ./...
go run honnef.co/go/tools/cmd/staticcheck@latest ./...

fmt:
gofmt -w .
83 changes: 46 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,46 @@
# Welcome to GoX CMS! 🎉

![GoXCMS Admin](https://i.imgur.com/ipHrr9x.png)

GoX CMS is a project that combines Go and HTMX to create a snappy, and enjoyable content management experience. It's a playground for experimenting, learning, and breaking things in a controlled environment.

## Features

- Blog
- Categories
- Tags
- Custom pages
- Comments
- Simple plugin system
- Shop Plugin
- Logger Plugin
- Latest Post Plugin
- Many different themes
- Media manager

## Quick Start 🏁

To get started with GoX CMS:

1. Clone the repository: `git clone https://github.com/ashba22/gox_cms.git`
2. Navigate into the project directory: `cd gox_cms`
3. Initialize the module: `go mod init goxcms`
4. Download the necessary dependencies: `go mod tidy`
5. Rename the `config-example` file to `config` located in the `config` folder.
6. Adjust the configuration settings in the `config` file according to your preferences.
7. Run the application: `go run main.go` or build and run `go build main.go && ./main`

Explore the code, experiment with changes, and don't hesitate to break things. Your discoveries and creations are what make this project thrive.


### TODO
CSRF Token
# Welcome to GoX CMS! 🎉

![GoXCMS Admin](https://i.imgur.com/ipHrr9x.png)

GoX CMS is a project that combines Go and HTMX to create a snappy, and enjoyable content management experience. It's a playground for experimenting, learning, and breaking things in a controlled environment.

## Features

- Blog
- Categories
- Tags
- Custom pages
- Comments
- Simple plugin system
- Shop Plugin
- Logger Plugin
- Latest Post Plugin
- Many different themes
- Media manager

## Quick Start 🏁

Requirements: Go (see `go.mod` for the version) and a C compiler (the SQLite driver uses cgo).

1. Clone the repository: `git clone https://github.com/PhantomPixelDev/gox_cms.git`
2. Navigate into the project directory: `cd gox_cms`
3. Copy the example config: `cp config/config-example.yaml config/config.yaml`
4. Set `app.secret` in `config/config.yaml` to a long random value, e.g. the output of `openssl rand -hex 32`. In `build.mode: production` the server refuses to start without one.
5. Run the application: `go run .` (or `make run`), then open http://localhost:3000.

On first start an `admin` account is created. Its password comes from the `ADMIN_PASSWORD` environment variable or `app.admin_password`; if neither is set, a random password is generated and printed once in the log. Change it after logging in.

## Development

- `make test` runs the test suite (`go test ./...`).
- `make lint` runs gofmt, `go vet` and staticcheck, the same checks as CI.
- `make fmt` formats the code.

### Configuration notes

- `app.url`: set it to the public URL. Cookies are marked `Secure` when it starts with `https://`, and it is used for the sitemap and as the default CORS origin.
- `server.trusted_proxies`: list your reverse proxies so client IPs are read from `X-Forwarded-For`.
- `server.prefork`: leave it off with SQLite.
- Custom pages are served as soon as they are saved; no restart is needed.

Explore the code, experiment with changes, and don't hesitate to break things. Your discoveries and creations are what make this project thrive.
19 changes: 14 additions & 5 deletions config/config-example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,24 @@ database:
server:
host: "127.0.0.1"
port: 3000
prefork: true
body_limit: 50000
# Prefork starts one process per CPU; keep it off with SQLite.
prefork: false
body_limit: 50
# IPs/CIDRs of reverse proxies allowed to set X-Forwarded-For. Leave empty
# when the app is reached directly.
trusted_proxies: []
build:
mode: production
app:
name: "GoXCMS"
version: "0.0.1"
domain: "localhost"
url: "http://localhost:3000"
secret: "change_this_secret"
hotload_custom_pages: false
# Signs login tokens. Required in production; generate one with `openssl rand -hex 32`.
secret: ""
# Password for the "admin" account created on first start. Leave empty to
# generate a random one, which is printed once to the log.
admin_password: ""
upload:
max_size_mb: 50
redis:
Expand All @@ -31,7 +38,9 @@ redis:
min_idle_conns: 10
max_conn_age: 0
cors:
allowed_origins: ["http://localhost:3000", "https://localhost:3000"]
# Defaults to app.url when empty.
allowed_origins: ["http://localhost:3000"]
allow_credentials: false
ratelimiter:
enabled: true
max_requests: 100
Expand Down
Empty file added data/.gitkeep
Empty file.
Binary file removed data/database.sqlite
Binary file not shown.
53 changes: 2 additions & 51 deletions database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@ import (
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"gorm.io/plugin/dbresolver"

"goxcms/model" // Update with your actual project path
"goxcms/model"

"github.com/spf13/viper"
)

// initDB initializes and returns a *gorm.DB instance.
// InitDB initializes and returns a *gorm.DB instance.
func InitDB() *gorm.DB {
var db *gorm.DB
var err error
Expand Down Expand Up @@ -44,11 +43,6 @@ func InitDB() *gorm.DB {
log.Fatalf("Failed to connect to database: %v", err)
}

db.Use(dbresolver.Register(dbresolver.Config{
Replicas: []gorm.Dialector{db.Dialector},
Policy: dbresolver.RandomPolicy{},
}))

err = db.AutoMigrate(
&model.User{},
&model.Post{},
Expand All @@ -68,48 +62,5 @@ func InitDB() *gorm.DB {
log.Fatalf("Failed to auto migrate: %v", err)
}

addForeignKeyConstraints(db)

return db
}

func addForeignKeyConstraints(db *gorm.DB) {
constraints := []struct {
Table1 string
Column1 string
Table2 string
Column2 string
}{
{"Post", "CategoryID", "Category", "ID"},
{"Post", "TagID", "Tag", "ID"},
{"Post", "UserID", "User", "ID"},
{"Post", "MenuID", "Menu", "ID"},
{"Post", "MenuItemID", "MenuItem", "ID"},
{"Post", "CustomPageID", "CustomPage", "ID"},
{"Category", "PostID", "Post", "ID"},
{"Category", "TagID", "Tag", "ID"},
{"Comment", "UserID", "User", "ID"},
{"Comment", "PostID", "Post", "ID"},
{"Tag", "PostID", "Post", "ID"},
{"Menu", "MenuItemID", "MenuItem", "ID"},
{"MenuItem", "MenuID", "Menu", "ID"},
{"CustomPage", "PostID", "Post", "ID"},
{"File", "PostID", "Post", "ID"},
{"BasicWebsiteInfo", "PostID", "Post", "ID"},
{"User", "RoleID", "Role", "ID"},
{"User", "PostID", "Post", "ID"},
{"User", "CommentID", "Comment", "ID"},
{"Role", "UserID", "User", "ID"},
}

for _, constraint := range constraints {
err := db.Migrator().CreateConstraint(constraint.Table1, constraint.Column1)
if err != nil {
log.Printf("Error creating constraint %s.%s: %v", constraint.Table1, constraint.Column1, err)
}
err = db.Migrator().CreateConstraint(constraint.Table2, constraint.Column2)
if err != nil {
log.Printf("Error creating constraint %s.%s: %v", constraint.Table2, constraint.Column2, err)
}
}
}
Loading
Loading