Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
d27ee1202b | |||
32ac21afd2 | |||
39959f041d | |||
124d702ec4 | |||
2587483733 | |||
575ec574dd | |||
815cf96374 | |||
2b6c24bc86 | |||
632774d051 | |||
1d6b419a15 | |||
2da7526265 | |||
92877b669e | |||
10168843e1 | |||
0889f9c7b1 | |||
084010e38c | |||
8158c195f5 | |||
174d15de5b | |||
56523795d5 | |||
32bdb3d709 | |||
b30d14af9a | |||
1220a37b60 | |||
a3e008c317 | |||
58498c87af | |||
fd9abb948a | |||
2b07f74cc1 | |||
ee4d2984dd | |||
dd80776bb1 | |||
d0e7ae9284 | |||
63433be0bb | |||
1a856e575e |
@ -12,8 +12,8 @@ jobs:
|
||||
name: check
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
contains(github.event.head_commit.message, 'bump:') == false &&
|
||||
contains(github.event.head_commit.message, 'Merge pull request') == false
|
||||
contains(gitea.event.head_commit.message, 'bump:') == false &&
|
||||
contains(gitea.event.head_commit.message, 'Merge pull request') == false
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
@ -65,14 +65,20 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: release # Wait for binary cache to propagate
|
||||
steps:
|
||||
- name: Get domain
|
||||
id: get_domain
|
||||
run: |
|
||||
DOMAIN=$(basename ${{ gitea.server_url }})
|
||||
echo $DOMAIN
|
||||
echo "domain=$DOMAIN" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
# list of Docker images to use as base name for tags
|
||||
images: |
|
||||
${{ github.repository }}
|
||||
ghcr.io/${{ github.repository }}
|
||||
${{ steps.get_domain.outputs.domain }}/${{ gitea.repository }}
|
||||
# generate Docker tags based on the following events/attributes
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
@ -81,9 +87,9 @@ jobs:
|
||||
- name: Login to Gitea Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ vars.URL }}
|
||||
username: ${{ vars.USERNAME }}
|
||||
password: ${{ secrets.PASSWORD }}
|
||||
registry: ${{ gitea.server_url }}
|
||||
username: ${{ gitea.actor }}
|
||||
password: ${{ secrets.PAT }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
@ -11,6 +11,8 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v31
|
||||
@ -28,12 +30,33 @@ jobs:
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git checkout -B update
|
||||
|
||||
- name: Update
|
||||
run: nix run .#update
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
title: update
|
||||
body: automatic update
|
||||
env:
|
||||
PAT: ${{ secrets.PAT }}
|
||||
run: |
|
||||
URL="${{ gitea.server_url }}"
|
||||
REPO_OWNER_SLASH_NAME="${{ gitea.repository }}"
|
||||
|
||||
if ! git ls-remote --exit-code origin update; then
|
||||
git push origin update --force
|
||||
|
||||
PR_RESPONSE=$(curl -s -X POST -H "Authorization: token $PAT" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"title":"update","body":"automatic update","head":"update","base":"main"}' \
|
||||
"https://$URL/api/v1/repos/$REPO_OWNER_SLASH_NAME/pulls")
|
||||
|
||||
PR_NUMBER=$(echo "$PR_RESPONSE" | jq -r '.number')
|
||||
|
||||
curl -s -X POST -H "Authorization: token $PAT" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"Do":"merge","merge_when_checks_succeed":true,"delete_branch_after_merge":true}' \
|
||||
"https://$URL/api/v1/repos/$REPO_OWNER_SLASH_NAME/pulls/$PR_NUMBER/merge"
|
||||
|
||||
else
|
||||
git push origin update --force
|
||||
fi
|
||||
|
7
.github/workflows/release.yaml
vendored
7
.github/workflows/release.yaml
vendored
@ -76,19 +76,12 @@ jobs:
|
||||
with:
|
||||
# list of Docker images to use as base name for tags
|
||||
images: |
|
||||
${{ github.repository }}
|
||||
ghcr.io/${{ github.repository }}
|
||||
# generate Docker tags based on the following events/attributes
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=semver,pattern={{version}}
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ vars.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
|
4
.github/workflows/update.yaml
vendored
4
.github/workflows/update.yaml
vendored
@ -37,12 +37,14 @@ jobs:
|
||||
run: nix run .#update
|
||||
|
||||
- name: Create Pull Request
|
||||
id: cpr
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
branch: update
|
||||
title: update
|
||||
body: automatic update
|
||||
|
||||
- name: Enable Automerge
|
||||
run: gh pr merge --merge --auto "1"
|
||||
run: gh pr merge --merge --auto "${{ steps.cpr.outputs.pull-request-number }}"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.PAT }}
|
||||
|
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@ -2,7 +2,7 @@
|
||||
"recommendations": [
|
||||
"golang.go",
|
||||
"dorzey.vscode-sqlfluff",
|
||||
"zxh404.vscode-proto3",
|
||||
"bufbuild.vscode-buf",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"svelte.svelte-vscode",
|
||||
"esbenp.prettier-vscode"
|
||||
|
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@ -18,8 +18,8 @@
|
||||
},
|
||||
|
||||
// Proto
|
||||
"[proto3]": {
|
||||
"editor.defaultFormatter": "zxh404.vscode-proto3"
|
||||
"[proto]": {
|
||||
"editor.defaultFormatter": "bufbuild.vscode-buf"
|
||||
},
|
||||
|
||||
// ESLint
|
||||
|
@ -26,4 +26,5 @@ WORKDIR /app
|
||||
# Copy /nix/store
|
||||
COPY --from=builder /tmp/nix-store-closure /nix/store
|
||||
COPY --from=builder /tmp/build/result /app
|
||||
|
||||
CMD ["/app/bin/trevstack"]
|
50
README.md
50
README.md
@ -7,6 +7,7 @@ This is a CRUD app to use as a template for starting projects
|
||||
- **Communicate anywhere**. Define a [protocol buffer](https://protobuf.dev/), and [Connect](https://connectrpc.com/) generates type-safe code to facilitate communication between the server and any client (web, mobile, embedded, etc). The protocol buffers can contain annotations to validate fields on the client and server. For clients that cannot use Connect, an OpenAPI spec is also generated
|
||||
- **Build anywhere**. The dev environment, testing and building is all declared in a single [Nix](https://nixos.org/) flake. Every developer and server can use the same environment
|
||||
- **Deploy anywhere**. CI/CD is already set up using github actions. New versions are automatically released for every major platform, along with a docker image. The binaries created require zero run-time dependencies and are relatively small (this app is 26 MiB)
|
||||
- Can be entirely self-hosted
|
||||
- Authentication is rolled in, including API key, fingerprint & passkey
|
||||
- Automatic database migration on startup
|
||||
- Light & dark modes with the [catppuccin](https://catppuccin.com/palette/) color palette
|
||||
@ -27,7 +28,7 @@ URL=http://localhost:5173
|
||||
DATABASE_URL=sqlite:/home/trev/.config/trevstack/sqlite.db
|
||||
```
|
||||
|
||||
4. Run `treli`
|
||||
4. Run `treli` to start the server & client
|
||||
|
||||
It's that simple. If you're feeling fancy, install [direnv](https://direnv.net/) and the dev environment will load automatically.
|
||||
|
||||
@ -37,11 +38,52 @@ It's that simple. If you're feeling fancy, install [direnv](https://direnv.net/)
|
||||
|
||||
- `nix run #bump [major | minor]`: bumps the current version up one. Defaults to "patch" (0.0.1 -> 0.0.2)
|
||||
|
||||
- `buf lint` & `buf generate`: Lints and generates code from protocol buffers
|
||||
- `nix build [#trevstack-(GOOS)-(GOARCH)]`: builds the application. Defaults to building for your current platform, but can be built to many by specifying the GOOS and GOARCH values
|
||||
|
||||
- `sqlc vet` & `sqlc generate`: Verifies and generates code from SQL files
|
||||
- `nix flake check`: runs all validations
|
||||
|
||||
- `dbmate new` & `dbmate up`: Creates a new migration file and runs pending migrations
|
||||
- `buf lint proto` & `buf generate`: lints and generates code from protocol buffers
|
||||
|
||||
- `sqlc vet` & `sqlc generate`: verifies and generates code from SQL files
|
||||
|
||||
- `dbmate new` & `dbmate up`: creates a new migration file and runs pending migrations
|
||||
|
||||
### Github Actions
|
||||
|
||||
To use github actions for CI/CD, you'll need to create a fine-grained personal access token for the repository with the permissions:
|
||||
|
||||
- Contents (read and write)
|
||||
- Pull requests (read and write)
|
||||
|
||||
And change some settings for the repository:
|
||||
|
||||
- General -> Allow auto-merge: true
|
||||
- Rules -> Rulesets -> New ruleset
|
||||
- Branch targeting criteria: Default
|
||||
- Branch rules
|
||||
- Require status checks to pass -> Add checks -> "check"
|
||||
- Actions -> General -> Workflow permissions
|
||||
- Read and write permissions: true
|
||||
- Allow GitHub Actions to create and approve pull requests: true
|
||||
- Secrets and variables -> Actions -> Repository secrets
|
||||
- PAT: (personal access token)
|
||||
|
||||
### Gitea Actions
|
||||
|
||||
To use gitea actions for CI/CD, you'll need to create an [API token](https://docs.gitea.com/development/api-usage) with the scopes:
|
||||
|
||||
- write:repository
|
||||
- write:package
|
||||
|
||||
And change some settings for the repository:
|
||||
|
||||
- Repository -> Delete pull request branch after merge by default: true
|
||||
- Branches -> Add New Rule
|
||||
- Protected Branch Name Pattern: main
|
||||
- Enable Status Check: true
|
||||
- Status check patterns: Check / check\*
|
||||
- Actions -> Secrets
|
||||
- PAT: (API token)
|
||||
|
||||
## Components
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
version: v2
|
||||
clean: true
|
||||
inputs:
|
||||
- directory: proto
|
||||
|
||||
managed:
|
||||
enabled: true
|
||||
override:
|
||||
|
4
client/package-lock.json
generated
4
client/package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "trevstack",
|
||||
"version": "0.0.26",
|
||||
"version": "0.0.34",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "trevstack",
|
||||
"version": "0.0.26",
|
||||
"version": "0.0.34",
|
||||
"devDependencies": {
|
||||
"@bufbuild/protovalidate": "^0.1.1",
|
||||
"@connectrpc/connect": "^2.0.2",
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "trevstack",
|
||||
"private": true,
|
||||
"version": "0.0.26",
|
||||
"version": "0.0.34",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
|
@ -3,8 +3,8 @@ servers:
|
||||
- url: /grpc
|
||||
info:
|
||||
title: Trevstack API
|
||||
version: 1.0.0
|
||||
description: API for trevstack
|
||||
version: 0.0.33
|
||||
description: API for Trevstack
|
||||
contact:
|
||||
name: Trev
|
||||
email: spam@trev.xyz
|
||||
|
@ -21,7 +21,7 @@
|
||||
...
|
||||
}: let
|
||||
pname = "trevstack";
|
||||
version = "0.0.26";
|
||||
version = "0.0.34";
|
||||
|
||||
build-systems = [
|
||||
"x86_64-linux"
|
||||
@ -127,7 +127,7 @@
|
||||
pname = "check-client";
|
||||
inherit version;
|
||||
src = ./client;
|
||||
npmDepsHash = "sha256-D95Q+FyfuF9GI2ALaAvmrP45g74aK5vg/bN0xyNyRZU=";
|
||||
npmDepsHash = "sha256-F5DHJvvASnHh03Soa4L4jaDIC9LC9sUX0wDasYHcuDE=";
|
||||
dontNpmInstall = true;
|
||||
|
||||
buildPhase = ''
|
||||
@ -190,7 +190,7 @@
|
||||
client = pkgs.buildNpmPackage {
|
||||
inherit pname version;
|
||||
src = ./client;
|
||||
npmDepsHash = "sha256-D95Q+FyfuF9GI2ALaAvmrP45g74aK5vg/bN0xyNyRZU=";
|
||||
npmDepsHash = "sha256-F5DHJvvASnHh03Soa4L4jaDIC9LC9sUX0wDasYHcuDE=";
|
||||
|
||||
installPhase = ''
|
||||
cp -r build "$out"
|
||||
@ -205,6 +205,7 @@
|
||||
|
||||
preBuild = ''
|
||||
cp -r ${client} client
|
||||
HOME=$PWD
|
||||
'';
|
||||
};
|
||||
in
|
||||
|
@ -3,7 +3,7 @@ servers:
|
||||
- url: /grpc
|
||||
info:
|
||||
title: Trevstack API
|
||||
version: 0.0.26
|
||||
version: 0.0.34
|
||||
description: API for Trevstack
|
||||
contact:
|
||||
name: Trev
|
||||
|
@ -1,6 +1,6 @@
|
||||
# For details on buf.yaml configuration, visit https://buf.build/docs/configuration/v2/buf-yaml
|
||||
version: v2
|
||||
modules:
|
||||
- path: proto
|
||||
- path: .
|
||||
deps:
|
||||
- buf.build/bufbuild/protovalidate
|
@ -68,7 +68,7 @@ func main() {
|
||||
// Serve gRPC Handlers
|
||||
api := http.NewServeMux()
|
||||
api.Handle(interceptors.WithCORS(user.NewAuthHandler(vi, sqlc, webAuthn, name, env.Key)))
|
||||
api.Handle(interceptors.WithCORS(user.NewHandler(vi, sqlc, webAuthn, env.Key)))
|
||||
api.Handle(interceptors.WithCORS(user.NewHandler(vi, sqlc, webAuthn, name, env.Key)))
|
||||
api.Handle(interceptors.WithCORS(item.NewHandler(vi, sqlc, env.Key)))
|
||||
|
||||
// Serve web interface
|
||||
|
@ -36,6 +36,7 @@ type Handler struct {
|
||||
db *sqlc.Queries
|
||||
webAuthn *webauthn.WebAuthn
|
||||
key []byte
|
||||
name string
|
||||
|
||||
sessions *map[int64]*webauthn.SessionData
|
||||
mu sync.Mutex
|
||||
@ -132,7 +133,7 @@ func (h *Handler) GetAPIKey(ctx context.Context, req *connect.Request[userv1.Get
|
||||
|
||||
// Generate JWT
|
||||
t := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
|
||||
Issuer: "trevstack",
|
||||
Issuer: h.name,
|
||||
Subject: strconv.FormatInt(user.ID, 10),
|
||||
IssuedAt: &jwt.NumericDate{
|
||||
Time: time.Now(),
|
||||
@ -341,8 +342,8 @@ func transportsToString(transports []protocol.AuthenticatorTransport) string {
|
||||
return s
|
||||
}
|
||||
|
||||
func NewHandler(vi *validate.Interceptor, db *sqlc.Queries, webauth *webauthn.WebAuthn, key string) (string, http.Handler) {
|
||||
interceptors := connect.WithInterceptors(interceptors.NewAuthInterceptor(key), vi)
|
||||
func NewHandler(vi *validate.Interceptor, db *sqlc.Queries, webauth *webauthn.WebAuthn, name string, key string) (string, http.Handler) {
|
||||
interceptors := connect.WithInterceptors(vi, interceptors.NewAuthInterceptor(key))
|
||||
|
||||
sd := map[int64]*webauthn.SessionData{}
|
||||
return userv1connect.NewUserServiceHandler(
|
||||
@ -350,6 +351,7 @@ func NewHandler(vi *validate.Interceptor, db *sqlc.Queries, webauth *webauthn.We
|
||||
db: db,
|
||||
webAuthn: webauth,
|
||||
key: []byte(key),
|
||||
name: name,
|
||||
|
||||
sessions: &sd,
|
||||
mu: sync.Mutex{},
|
||||
|
Reference in New Issue
Block a user