Compare commits

..

No commits in common. "main" and "v0.0.23" have entirely different histories.

29 changed files with 1563 additions and 969 deletions

View File

@ -1,21 +0,0 @@
name: "Initialize"
description: "Install nix & use cachix"
inputs:
token:
description: "cachix auth token"
required: true
runs:
using: "composite"
steps:
- name: Install nix
uses: cachix/install-nix-action@v31
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: Use cachix
uses: cachix/cachix-action@v16
with:
name: trevstack
authToken: "${{ inputs.token }}"

View File

@ -1,63 +0,0 @@
name: "Docker Push"
description: "Push to docker registry"
inputs:
server_url:
required: true
repository:
required: true
tag:
required: true
runs:
using: "composite"
steps:
- name: Set env
shell: bash
run: |
REGISTRY=$(basename ${{ inputs.server_url }})
NR=${{ inputs.repository }}
NAMESPACE="${NR%%/*}"
REPOSITORY="${NR##*/}"
TAG=${{ inputs.tag }}
VERSION=${TAG#v}
echo "REGISTRY=${REGISTRY}" >> $GITHUB_ENV
echo "NAMESPACE=${NAMESPACE}" >> $GITHUB_ENV
echo "REPOSITORY=${REPOSITORY}" >> $GITHUB_ENV
echo "VERSION=${VERSION}" >> $GITHUB_ENV
- name: Push images
shell: bash
run: |
docker image tag $REPOSITORY:$VERSION-amd64 $REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION-amd64
docker push $REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION-amd64
docker image tag $REPOSITORY:$VERSION-arm64 $REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION-arm64
docker push $REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION-arm64
docker image tag $REPOSITORY:$VERSION-arm $REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION-arm
docker push $REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION-arm
- name: Push manifest
shell: bash
run: |
docker manifest create $REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION \
$REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION-amd64 \
$REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION-arm64 \
$REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION-arm
docker manifest annotate $REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION $REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION-amd64 --arch amd64
docker manifest annotate $REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION $REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION-arm64 --arch arm64
docker manifest annotate $REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION $REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION-arm --arch arm
docker manifest push $REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION
docker manifest create $REGISTRY/$NAMESPACE/$REPOSITORY:latest \
$REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION-amd64 \
$REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION-arm64 \
$REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION-arm
docker manifest annotate $REGISTRY/$NAMESPACE/$REPOSITORY:latest $REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION-amd64 --arch amd64
docker manifest annotate $REGISTRY/$NAMESPACE/$REPOSITORY:latest $REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION-arm64 --arch arm64
docker manifest annotate $REGISTRY/$NAMESPACE/$REPOSITORY:latest $REGISTRY/$NAMESPACE/$REPOSITORY:$VERSION-arm --arch arm
docker manifest push $REGISTRY/$NAMESPACE/$REPOSITORY:latest

14
.dockerignore Normal file
View File

@ -0,0 +1,14 @@
.env
/docker-compose.*
/result*
/.direnv/
/build/
# Client
/client/node_modules/
/client/.svelte-kit/
# Server
/server/client/
/server/tmp/
/server/build/

View File

@ -1,23 +0,0 @@
name: Check
on:
push:
branches:
- main
pull_request:
types: [opened, reopened, edited, auto_merge_enabled]
jobs:
check:
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
steps:
- uses: actions/checkout@v4
- uses: ./.actions/init
with:
token: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- run: nix flake check

View File

@ -1,68 +0,0 @@
name: Release
on:
push:
tags:
- "*"
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.actions/init
with:
token: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- run: nix flake check
release:
runs-on: ubuntu-latest
needs: check
steps:
- uses: actions/checkout@v4
- uses: ./.actions/init
with:
token: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- run: >
nix build
.#trevstack-linux-amd64
.#trevstack-linux-arm64
.#trevstack-linux-arm
.#trevstack-windows-amd64
.#trevstack-darwin-amd64
.#trevstack-darwin-arm64
- uses: akkuman/gitea-release-action@v1
with:
files: |-
result*/bin/*
package:
runs-on: ubuntu-latest
needs: release
steps:
- uses: actions/checkout@v4
- uses: ./.actions/init
with:
token: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- uses: docker/login-action@v3
with:
registry: ${{ github.server_url }}
username: ${{ github.actor }}
password: ${{ secrets.PAT }}
- name: Build & load images
run: |
nix build .#trevstack-linux-amd64-image && ./result | docker load
nix build .#trevstack-linux-arm64-image && ./result | docker load
nix build .#trevstack-linux-arm-image && ./result | docker load
- name: Push images
uses: ./.actions/push
with:
server_url: ${{ github.server_url }}
repository: ${{ github.repository }}
tag: ${{ github.ref_name }}

View File

@ -1,50 +0,0 @@
name: Update
on:
schedule:
- cron: "0 0 * * *"
workflow_dispatch:
jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.actions/init
with:
token: "${{ secrets.CACHIX_AUTH_TOKEN }}"
# https://github.com/actions/checkout/issues/13
- name: Set git config
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git checkout -B update
- run: nix run .#update
- name: Create pull request
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"}' \
"$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}' \
"$URL/api/v1/repos/$REPO_OWNER_SLASH_NAME/pulls/$PR_NUMBER/merge"
else
git push origin update --force
fi

View File

@ -1,23 +0,0 @@
name: Check
on:
push:
branches:
- main
pull_request:
types: [opened, reopened, edited, auto_merge_enabled]
jobs:
check:
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
steps:
- uses: actions/checkout@v4
- uses: ./.actions/init
with:
token: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- run: nix flake check

28
.github/workflows/lint.yaml vendored Normal file
View File

@ -0,0 +1,28 @@
name: Lint Workflow
on:
push:
branches:
- main
pull_request:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@v31
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: Use Cachix
uses: cachix/cachix-action@v16
with:
name: trevstack
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- name: Run checks
run: nix flake check

View File

@ -1,35 +1,34 @@
name: Release
name: Release Workflow
on:
push:
tags:
- "*"
- '*'
permissions:
contents: write
packages: write
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.actions/init
with:
token: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- run: nix flake check
release:
runs-on: ubuntu-latest
needs: check
steps:
- uses: actions/checkout@v4
- uses: ./.actions/init
with:
token: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: Checkout
uses: actions/checkout@v4
- run: >
- name: Install Nix
uses: cachix/install-nix-action@v31
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: Use Cachix
uses: cachix/cachix-action@v16
with:
name: trevstack
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- name: Build
run: >
nix build
.#trevstack-linux-amd64
.#trevstack-linux-arm64
@ -38,36 +37,52 @@ jobs:
.#trevstack-darwin-amd64
.#trevstack-darwin-arm64
- uses: softprops/action-gh-release@v2
- name: Create Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
files: |-
result*/bin/*
# https://docs.docker.com/build/ci/github-actions/manage-tags-labels/
package:
runs-on: ubuntu-latest
needs: release
needs: release # Wait for binary cache to propagate
steps:
- uses: actions/checkout@v4
- uses: ./.actions/init
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
token: "${{ secrets.CACHIX_AUTH_TOKEN }}"
# 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}}
- uses: docker/login-action@v3
- 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:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build & load images
run: |
nix build .#trevstack-linux-amd64-image && ./result | docker load
nix build .#trevstack-linux-arm64-image && ./result | docker load
nix build .#trevstack-linux-arm-image && ./result | docker load
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Push images
uses: ./.actions/push
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push
uses: docker/build-push-action@v6
with:
server_url: ghcr.io
repository: ${{ github.repository }}
tag: ${{ github.ref_name }}
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

View File

@ -1,40 +1,41 @@
name: Update
name: Update Workflow
on:
schedule:
- cron: "0 0 * * *"
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.actions/init
- name: Checkout
uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@v31
with:
token: "${{ secrets.CACHIX_AUTH_TOKEN }}"
nix_path: nixpkgs=channel:nixos-unstable
- name: Use Cachix
uses: cachix/cachix-action@v16
with:
name: trevstack
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
# https://github.com/actions/checkout/issues/13
- name: Set git config
- name: Set Git Config
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Update
run: nix run .#update
- run: nix run .#update
- name: Create pull request
id: cpr
- name: Create Pull Request
uses: peter-evans/create-pull-request@v7
with:
branch: update
title: update
body: automatic update
delete-branch: true
title: Bump deps
- name: Enable automerge
run: gh pr merge --merge --auto "${{ steps.cpr.outputs.pull-request-number }}"
env:
GH_TOKEN: ${{ secrets.PAT }}

View File

@ -3,25 +3,7 @@
git_root=$(git rev-parse --show-toplevel)
git_version=$(git describe --tags --abbrev=0)
version=${git_version#v}
major=$(echo "${version}" | cut -d . -f1)
minor=$(echo "${version}" | cut -d . -f2)
patch=$(echo "${version}" | cut -d . -f3)
case "${1-patch}" in
major) major=$((major + 1)) ;;
minor) minor=$((minor + 1)) ;;
*) patch=$((patch + 1)) ;;
esac
next_version="${major}.${minor}.${patch}"
echo "${version} -> ${next_version}"
echo "bumping openapi"
cd "${git_root}"
sed -i -e "s/${version}/${next_version}/g" openapi.yaml
sed -i -e "s/${version}/${next_version}/g" client/static/openapi/openapi.yaml
git add openapi.yaml
git add client/static/openapi/openapi.yaml
next_version=$(echo "${version}" | awk -F. -v OFS=. '{$NF += 1 ; print}')
echo "bumping client"
cd "${git_root}/client"

View File

@ -11,14 +11,6 @@ if ! git diff --exit-code flake.lock; then
git commit -m "build(nix): updated nix dependencies"
fi
echo "updating protobuf deps"
cd "${git_root}/proto"
buf dep update
if ! git diff --exit-code buf.lock; then
git add buf.lock
git commit -m "build(buf): updated buf dependencies"
fi
echo "updating client"
cd "${git_root}/client"
npm update --save && npm i

View File

@ -2,7 +2,6 @@
"recommendations": [
"golang.go",
"dorzey.vscode-sqlfluff",
"bufbuild.vscode-buf",
"dbaeumer.vscode-eslint",
"svelte.svelte-vscode",
"esbenp.prettier-vscode"

View File

@ -4,7 +4,6 @@
// Go
"go.lintTool": "revive",
"go.formatTool": "goimports",
"go.buildTags": "dev",
"go.lintFlags": ["--config=server/revive.toml"],
"gopls": { "ui.semanticTokens": true },
"[go]": {
@ -13,14 +12,6 @@
// SQLFluff
"sqlfluff.config": "server/db/.sqlfluff",
"[sql]": {
"editor.defaultFormatter": "dorzey.vscode-sqlfluff"
},
// Proto
"[proto]": {
"editor.defaultFormatter": "bufbuild.vscode-buf"
},
// ESLint
"eslint.workingDirectories": ["./client"],

29
Dockerfile Normal file
View File

@ -0,0 +1,29 @@
# Nix builder
FROM nixos/nix:latest AS builder
# Copy our source and setup our working dir.
COPY . /tmp/build
WORKDIR /tmp/build
# Build our Nix environment
RUN nix \
--extra-experimental-features "nix-command flakes" \
--option filter-syscalls false \
--accept-flake-config \
build
# Copy the Nix store closure into a directory. The Nix store closure is the
# entire set of Nix store values that we need for our build.
RUN mkdir /tmp/nix-store-closure
RUN cp -R $(nix-store -qR result/) /tmp/nix-store-closure
# Final image is based on scratch. We copy a bunch of Nix dependencies
# but they're fully self-contained so we don't need Nix anymore.
FROM scratch
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"]

21
LICENSE
View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) Trev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

125
README.md
View File

@ -1,125 +0,0 @@
## TrevStack
This is a CRUD app to use as a template for starting projects
### Features
- **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
- Really good at running as a [progressive web app](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps)
While I personally prefer using a Svelte frontend and Go backend, feel free to swap them out with whatever you like, you'll be surprised how easy it is.
## Getting Started
1. [Install Nix](https://nixos.org/download/)
2. Run `nix develop`
3. Create a `server/.env` file that looks something like
```env
KEY=changeme
PORT=8080
URL=http://localhost:5173
DATABASE_URL=sqlite:/home/trev/.config/trevstack/sqlite.db
```
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.
### Useful Commands
- `nix run #update`: updates all of the dependencies
- `nix run #bump [major | minor]`: bumps the current version up one. Defaults to "patch" (0.0.1 -> 0.0.2)
- `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
- `nix flake check`: runs all validations
- `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
### Client
- **svelte 5** [[docs](https://svelte.dev/docs/svelte)] UI framework
- **tailwind 4** [[docs](https://tailwindcss.com/)] CSS framework
- **bits ui** [[docs](https://bits-ui.com/docs/)] headless components
- [components](https://github.com/spotdemo4/trevstack/tree/main/client/src/lib/ui) from **shadcn-svelte** [[docs](https://www.shadcn-svelte.com/docs)] altered to work with tailwind 4, fit the [catppuccin](https://catppuccin.com/palette/) color palette, and use [shallow routing](https://svelte.dev/docs/kit/shallow-routing)
- **connect rpc** [[docs](https://connectrpc.com/docs/web/)] to communicating with the server
- **protovalidate-es** [[docs](https://github.com/bufbuild/protovalidate-es)], along with a function [coolforms](https://github.com/spotdemo4/trevstack/blob/main/client/src/lib/coolforms/) to emulate the library [sveltekit-superforms](https://superforms.rocks/)
- **simplewebauthn** [[docs](https://simplewebauthn.dev/docs/packages/browser)] for passkey authentication
- **scalar** [[docs](https://github.com/scalar/scalar)] for displaying openapi specs
- **tw-animate-css** [[docs](https://github.com/Wombosvideo/tw-animate-css)] to animate with just tailwind classes
- vite [[docs](https://vite.dev/)] for dev server and bundling
- eslint [[docs](https://eslint.org/)] for linting
- prettier [[docs](https://prettier.io/)] for formatting
- prettier-plugin-svelte [[docs](https://github.com/sveltejs/prettier-plugin-svelte)]
- prettier-plugin-tailwindcss [[docs](https://github.com/tailwindlabs/prettier-plugin-tailwindcss)]
- prettier-plugin-sort-imports [[docs](https://github.com/IanVS/prettier-plugin-sort-imports)]
### Server
- **go** [[docs](https://go.dev/doc/)]
- **connect rpc** [[docs](https://connectrpc.com/docs/go/)] to serve gRPC & HTTP requests
- **protovalidate-go** [[docs](https://github.com/bufbuild/protovalidate-go)] for validating those requests
- **sqlc** [[docs](https://docs.sqlc.dev/en/latest/)] because writing the SQL yourself is better than an ORM
- **go-webauthn** [[docs](https://github.com/go-webauthn/webauthn)] because webauthn is hard
- **dbmate** [[docs](https://github.com/amacneil/dbmate)] for database migrations
- revive [[docs](https://github.com/mgechev/revive)] for linting
### Protocol Buffers / gRPC
- **buf** [[docs](https://buf.build/docs/)] CLI for linting & code generation
- **protovalidate** [[docs](https://buf.build/docs/protovalidate/)] provides annotations to validate proto messages & fields
- **protoc-gen-connect-openapi** [[docs](https://github.com/sudorandom/protoc-gen-connect-openapi)] generates openapi specs
- protoc-gen-go [[docs](https://pkg.go.dev/google.golang.org/protobuf)]
- protoc-gen-connect-go [[docs](https://connectrpc.com/docs/go)]
- protoc-gen-es [[docs](https://connectrpc.com/docs/web/)]

View File

@ -1,8 +1,5 @@
version: v2
clean: true
inputs:
- directory: proto
managed:
enabled: true
override:
@ -30,5 +27,5 @@ plugins:
out: client/static/openapi
strategy: all
opt:
- base=openapi.yaml
- base=openapi.base.yaml
- path=openapi.yaml

View File

@ -1,6 +1,6 @@
# For details on buf.yaml configuration, visit https://buf.build/docs/configuration/v2/buf-yaml
version: v2
modules:
- path: .
- path: proto
deps:
- buf.build/bufbuild/protovalidate

1745
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{
"name": "trevstack",
"private": true,
"version": "0.0.47",
"version": "0.0.23",
"type": "module",
"scripts": {
"dev": "vite dev",
@ -21,29 +21,30 @@
"@eslint/js": "^9.18.0",
"@ianvs/prettier-plugin-sort-imports": "^4.4.1",
"@lucide/svelte": "^0.479.0",
"@scalar/api-reference": "^1.28.34",
"@scalar/api-reference": "^1.28.33",
"@simplewebauthn/browser": "^13.1.0",
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/kit": "^2.21.1",
"@sveltejs/kit": "^2.21.0",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@tailwindcss/vite": "^4.1.7",
"bits-ui": "^1.5.3",
"@tailwindcss/vite": "^4.1.6",
"bits-ui": "^1.4.8",
"clsx": "^2.1.1",
"eslint": "^9.27.0",
"eslint": "^9.26.0",
"eslint-config-prettier": "^10.1.5",
"eslint-plugin-svelte": "^3.8.1",
"eslint-plugin-svelte": "^3.6.0",
"fast-deep-equal": "^3.1.3",
"globals": "^16.1.0",
"mode-watcher": "^1.0.7",
"prettier": "^3.5.3",
"prettier-plugin-svelte": "^3.4.0",
"prettier-plugin-svelte": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.11",
"svelte": "^5.31.1",
"svelte-check": "^4.2.1",
"svelte": "^5.28.6",
"svelte-check": "^4.1.7",
"svelte-sonner": "^0.3.28",
"tailwind-merge": "^3.3.0",
"tailwind-variants": "^1.0.0",
"tailwindcss": "^4.0.13",
"tw-animate-css": "^1.3.0",
"tw-animate-css": "^1.2.9",
"typescript": "^5.8.3",
"typescript-eslint": "^8.32.1",
"vite": "^6.3.5"

View File

@ -3,8 +3,8 @@ servers:
- url: /grpc
info:
title: Trevstack API
version: 0.0.33
description: API for Trevstack
version: 1.0.0
description: API for trevstack
contact:
name: Trev
email: spam@trev.xyz

6
flake.lock generated
View File

@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1747542820,
"narHash": "sha256-GaOZntlJ6gPPbbkTLjbd8BMWaDYafhuuYRNrxCGnPJw=",
"lastModified": 1746904237,
"narHash": "sha256-3e+AVBczosP5dCLQmMoMEogM57gmZ2qrVSrmq9aResQ=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "292fa7d4f6519c074f0a50394dbbe69859bb6043",
"rev": "d89fc19e405cb2d55ce7cc114356846a0ee5e956",
"type": "github"
},
"original": {

View File

@ -21,7 +21,7 @@
...
}: let
pname = "trevstack";
version = "0.0.47";
version = "0.0.23";
build-systems = [
"x86_64-linux"
@ -29,17 +29,6 @@
"x86_64-darwin"
"aarch64-darwin"
];
forSystem = f:
nixpkgs.lib.genAttrs build-systems (
system:
f {
inherit system;
pkgs = import nixpkgs {
inherit system;
};
}
);
host-systems = [
{
GOOS = "linux";
@ -66,6 +55,16 @@
GOARCH = "arm64";
}
];
forSystem = f:
nixpkgs.lib.genAttrs build-systems (
system:
f {
inherit system;
pkgs = import nixpkgs {
inherit system;
};
}
);
in {
devShells = forSystem ({pkgs, ...}: let
protoc-gen-connect-openapi = pkgs.buildGoModule {
@ -81,12 +80,9 @@
in {
default = pkgs.mkShell {
packages = with pkgs; [
treli.packages."${system}".default
git
# Nix
nix-update
alejandra
treli.packages."${system}".default
# Server
go
@ -95,7 +91,7 @@
revive
sqlc
# Database
# database
sqlite
dbmate
sqlfluff
@ -130,7 +126,7 @@
pname = "check-client";
inherit version;
src = ./client;
npmDepsHash = "sha256-dl32ehKir0dZ4uiJ4s59xPIIbMkkZtH9dlTm4W0PZag=";
npmDepsHash = "sha256-Te5HGbp7mKG3p1P4O266IpoPPBN7oQ/dZbttdgKbgWs=";
dontNpmInstall = true;
buildPhase = ''
@ -163,10 +159,9 @@
runtimeInputs = with pkgs; [
git
nix
nix-update
go
buf
nodejs_22
go
nix-update
];
text = builtins.readFile ./.scripts/update.sh;
});
@ -193,7 +188,7 @@
client = pkgs.buildNpmPackage {
inherit pname version;
src = ./client;
npmDepsHash = "sha256-dl32ehKir0dZ4uiJ4s59xPIIbMkkZtH9dlTm4W0PZag=";
npmDepsHash = "sha256-Te5HGbp7mKG3p1P4O266IpoPPBN7oQ/dZbttdgKbgWs=";
installPhase = ''
cp -r build "$out"
@ -208,11 +203,13 @@
preBuild = ''
cp -r ${client} client
HOME=$PWD
'';
};
binaries = builtins.listToAttrs (builtins.map (x: {
in
{
default = server;
}
// builtins.listToAttrs (builtins.map (x: {
name = "${pname}-${x.GOOS}-${x.GOARCH}";
value = server.overrideAttrs {
nativeBuildInputs =
@ -235,28 +232,7 @@
'';
};
})
host-systems);
images = builtins.listToAttrs (builtins.map (x: {
name = "${pname}-${x.GOOS}-${x.GOARCH}-image";
value = pkgs.dockerTools.streamLayeredImage {
name = "${pname}";
tag = "${version}-${x.GOARCH}";
created = "now";
architecture = "${x.GOARCH}";
contents = [binaries."${pname}-${x.GOOS}-${x.GOARCH}"];
config = {
Cmd = ["${binaries."${pname}-${x.GOOS}-${x.GOARCH}"}/bin/${pname}-${x.GOOS}-${x.GOARCH}-${version}"];
};
};
})
(builtins.filter (x: x.GOOS == "linux") host-systems));
in
{
default = server;
}
// binaries
// images
host-systems)
);
};
}

View File

@ -3,8 +3,8 @@ servers:
- url: /grpc
info:
title: Trevstack API
version: 0.0.47
description: API for Trevstack
version: 1.0.0
description: API for trevstack
contact:
name: Trev
email: spam@trev.xyz
@ -15,4 +15,4 @@ components:
scheme: bearer
bearerFormat: JWT
security:
- bearerAuth: []
- bearerAuth: []

16
server/bobgen.yaml Normal file
View File

@ -0,0 +1,16 @@
wipe: true
replacements:
- match:
db_type: "INTEGER"
replace: "int64"
- match:
db_type: "INTEGER"
nullable: true
replace: "int64"
sql:
dialect: sqlite
dir: db
output: internal/models

View File

@ -29,8 +29,6 @@ import (
)
func main() {
name := "TrevStack"
// Get env
env, err := getEnv()
if err != nil {
@ -51,7 +49,7 @@ func main() {
// Create webauthn
webAuthn, err := webauthn.New(&webauthn.Config{
RPDisplayName: name,
RPDisplayName: env.Name,
RPID: env.URL.Hostname(),
RPOrigins: []string{env.URL.String()},
})
@ -65,10 +63,10 @@ func main() {
log.Fatalf("failed to create validator: %s", err.Error())
}
// Serve gRPC Handlers
// 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, name, env.Key)))
api.Handle(interceptors.WithCORS(user.NewAuthHandler(vi, sqlc, webAuthn, env.Name, env.Key)))
api.Handle(interceptors.WithCORS(user.NewHandler(vi, sqlc, webAuthn, env.Key)))
api.Handle(interceptors.WithCORS(item.NewHandler(vi, sqlc, env.Key)))
// Serve web interface
@ -110,6 +108,7 @@ func main() {
type env struct {
Port string
Key string
Name string
URL *url.URL
DatabaseURL string
}
@ -124,6 +123,7 @@ func getEnv() (*env, error) {
env := env{
Port: os.Getenv("PORT"),
Key: os.Getenv("KEY"),
Name: os.Getenv("NAME"),
DatabaseURL: os.Getenv("DATABASE_URL"),
}
@ -135,6 +135,10 @@ func getEnv() (*env, error) {
if env.Key == "" {
return nil, errors.New("env 'KEY' not found")
}
if env.Name == "" {
env.Name = "trevstack"
log.Printf("env 'NAME' not found, defaulting to %s\n", env.Name)
}
if env.DatabaseURL == "" {
return nil, errors.New("env 'DATABASE_URL' not found")
}

View File

@ -36,7 +36,6 @@ type Handler struct {
db *sqlc.Queries
webAuthn *webauthn.WebAuthn
key []byte
name string
sessions *map[int64]*webauthn.SessionData
mu sync.Mutex
@ -133,7 +132,7 @@ func (h *Handler) GetAPIKey(ctx context.Context, req *connect.Request[userv1.Get
// Generate JWT
t := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
Issuer: h.name,
Issuer: "trevstack",
Subject: strconv.FormatInt(user.ID, 10),
IssuedAt: &jwt.NumericDate{
Time: time.Now(),
@ -342,8 +341,8 @@ func transportsToString(transports []protocol.AuthenticatorTransport) string {
return s
}
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))
func NewHandler(vi *validate.Interceptor, db *sqlc.Queries, webauth *webauthn.WebAuthn, key string) (string, http.Handler) {
interceptors := connect.WithInterceptors(interceptors.NewAuthInterceptor(key), vi)
sd := map[int64]*webauthn.SessionData{}
return userv1connect.NewUserServiceHandler(
@ -351,7 +350,6 @@ 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{},