├── .dockerignore
├── .github
└── workflows
│ └── docker.yaml
├── .gitignore
├── Dockerfile
├── Makefile
├── README.md
├── assets
├── app.js
├── index.html
└── style.css
├── go.mod
├── go.sum
├── k8s
└── knative.yaml
├── main.go
└── prisma
├── db
└── .gitignore
└── schema.prisma
/.dockerignore:
--------------------------------------------------------------------------------
1 | .env
2 | go-prisma-example
3 | go-prisma-example.exe
4 | test.sh
5 | prisma/db
6 | k8s
7 |
--------------------------------------------------------------------------------
/.github/workflows/docker.yaml:
--------------------------------------------------------------------------------
1 | name: Publish Docker image
2 |
3 | on:
4 | push:
5 | tags:
6 | - 'v*'
7 |
8 | jobs:
9 | push_to_registry:
10 | name: Docker
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Check out the repo
14 | uses: actions/checkout@v3
15 |
16 | - name: Set up QEMU
17 | uses: docker/setup-qemu-action@v1
18 |
19 | - name: Set up Docker Buildx
20 | id: buildx
21 | uses: docker/setup-buildx-action@v1
22 |
23 | - name: Log in to GitHub Container Registry
24 | uses: docker/login-action@v2
25 | with:
26 | registry: ghcr.io
27 | username: ${{ github.actor }}
28 | password: ${{ secrets.GITHUB_TOKEN }}
29 |
30 | - name: Extract metadata (tags, labels) for Docker
31 | id: meta
32 | uses: docker/metadata-action@v3
33 | with:
34 | images: ghcr.io/${{ github.repository }}
35 |
36 | - name: Build and push Docker image
37 | uses: docker/build-push-action@v2
38 | with:
39 | context: .
40 | platforms: linux/amd64
41 | builder: ${{ steps.buildx.outputs.name }}
42 | push: ${{ github.event_name != 'pull_request' }}
43 | tags: |
44 | ghcr.io/${{ github.repository }}:latest
45 | ${{ steps.meta.outputs.tags }}
46 | labels: ${{ steps.meta.outputs.labels }}
47 | cache-from: type=gha
48 | cache-to: type=gha,mode=max # mode=maxを有効にすると、中間ステージまで含めてキャッシュできる
49 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 | go-prisma-example
3 | go-prisma-example.exe
4 | test.sh
5 | k8s/secret.yaml
6 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # syntax=docker/dockerfile:1.4
2 |
3 | FROM golang:1.22-alpine AS build-dev
4 | WORKDIR /go/src/app
5 | COPY go.mod go.sum ./
6 | RUN go mod download
7 | RUN apk add --no-cache upx || \
8 | RUN go run github.com/steebchen/prisma-client-go prefetch
9 | COPY ./ ./
10 | RUN go run github.com/steebchen/prisma-client-go generate
11 | RUN CGO_ENABLED=0 go install -buildvcs=false -trimpath -ldflags '-w -s -extldflags "-static"'
12 | #RUN CGO_ENABLED=0 GOOS=linux go install -buildvcs=false -trimpath -installsuffix cgo -ldflags '-extldflags "-static"' -o app .
13 |
14 | RUN [ -e /usr/bin/upx ] && upx /go/bin/go-prisma-example || echo
15 | FROM scratch
16 | COPY --link --from=build-dev /go/bin/go-prisma-example /go/bin/go-prisma-example
17 | COPY --from=build-dev /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
18 | CMD ["/go/bin/go-prisma-example"]
19 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | BIN := go-prisma-example
2 | VERSION := $$(make -s show-version)
3 | CURRENT_REVISION := $(shell git rev-parse --short HEAD)
4 | BUILD_LDFLAGS := "-s -w -X main.revision=$(CURRENT_REVISION)"
5 | GOBIN ?= $(shell go env GOPATH)/bin
6 | export GO111MODULE=on
7 |
8 | .PHONY: all
9 | all: clean build
10 |
11 | .PHONY: build
12 | build:
13 | go build -ldflags=$(BUILD_LDFLAGS) -o $(BIN) .
14 |
15 | .PHONY: install
16 | install:
17 | go install -ldflags=$(BUILD_LDFLAGS) .
18 |
19 | .PHONY: show-version
20 | show-version: $(GOBIN)/gobump
21 | gobump show -r .
22 |
23 | $(GOBIN)/gobump:
24 | go install github.com/x-motemen/gobump/cmd/gobump@latest
25 |
26 | .PHONY: cross
27 | cross: $(GOBIN)/goxz
28 | goxz -n $(BIN) -pv=v$(VERSION) -build-ldflags=$(BUILD_LDFLAGS) .
29 |
30 | $(GOBIN)/goxz:
31 | go install github.com/Songmu/goxz/cmd/goxz@latest
32 |
33 | .PHONY: test
34 | test: build
35 | go test -v ./...
36 |
37 | .PHONY: clean
38 | clean:
39 | rm -rf $(BIN) goxz
40 | go clean
41 |
42 | .PHONY: bump
43 | bump: $(GOBIN)/gobump
44 | ifneq ($(shell git status --porcelain),)
45 | $(error git workspace is dirty)
46 | endif
47 | ifneq ($(shell git rev-parse --abbrev-ref HEAD),main)
48 | $(error current branch is not main)
49 | endif
50 | @gobump up -w .
51 | git commit -am "bump up version to $(VERSION)"
52 | git tag "v$(VERSION)"
53 | git push origin main
54 | git push origin "refs/tags/v$(VERSION)"
55 |
56 | .PHONY: upload
57 | upload: $(GOBIN)/ghr
58 | ghr "v$(VERSION)" goxz
59 |
60 | $(GOBIN)/ghr:
61 | go install github.com/tcnksm/ghr@latest
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # go-prisma-example
2 |
3 | Example Go app using [prisma](https://www.prisma.io/), [lit-html](https://lit-html.polymer-project.org/), and [ky](https://github.com/sindresorhus/ky).
4 |
5 | ## Usage
6 |
7 | First of all, install PostgreSQL.
8 | Then try following commands.
9 |
10 | ```console
11 | $ go generate
12 | $ go build
13 | $ echo 'DATABASE_URL=postgresql://yourname:yourname@localhost:5432/postgres' > .env
14 | $ npx prisma db push
15 | $ ./go-prisma-example
16 | ```
17 |
18 | Then you can use [TODO app](http://localhost:8989)! :tada:
19 |
20 | ## Reference
21 |
22 | * [Blog post(ja)](https://zenn.dev/mattn/articles/1c4eb193d81a3a)
23 |
24 | ## License
25 |
26 | MIT
27 |
28 | assets/app.js is based on @ryohey's [lit-html-todo](https://github.com/ryohey/lit-html-todo)
29 |
30 | ## Author
31 |
32 | Yasuhiro Matsumoto (a.k.a. mattn)
33 |
--------------------------------------------------------------------------------
/assets/app.js:
--------------------------------------------------------------------------------
1 | import {html, render} from 'https://unpkg.com/lit-html@3.1.4/lit-html.js';
2 | import {classMap} from 'https://unpkg.com/lit-html@3.1.4/directives/class-map.js';
3 | import ky from 'https://unpkg.com/ky@1.3.0/distribution/index.js';
4 |
5 | const TaskItem = (
6 | task,
7 | onCheck,
8 | onClickRemove,
9 | ) => html`
10 |
11 |
onCheck(e.currentTarget.checked)}
14 | .checked=${task.completed}
15 | />
16 |
${task.text}
17 |
×
18 |
19 | `
20 |
21 | const TaskList = (
22 | tasks,
23 | onCheck,
24 | onClickRemove,
25 | ) => html`
26 |
27 | ${tasks.map(t =>
28 | TaskItem(t, checked => onCheck(t.id, checked), () => onClickRemove(t.id))
29 | )}
30 |
31 | `
32 |
33 | const NewTask = (
34 | inputText,
35 | onInput,
36 | onKeyPress,
37 | ) => html`
38 |
39 | onInput(e.currentTarget.value)}
43 | @keypress=${onKeyPress}
44 | placeholder="what we have to do?"
45 | />
46 |
47 | `
48 |
49 | const App = () => html`
50 |
51 |
ToDos
52 | ${NewTask(
53 | store().inputText,
54 | inputText => store({ inputText }),
55 | e => {
56 | if (e.key === "Enter") {
57 | (async () => {
58 | const state = store();
59 | const task = await ky.post('/tasks', {
60 | json: { text: state.inputText }
61 | }).json();
62 | store({
63 | tasks: [...state.tasks, task],
64 | inputText: ""
65 | })
66 | })();
67 | }
68 | }
69 | )}
70 | ${TaskList(
71 | store().tasks,
72 | (id, completed) => {
73 | (async () => {
74 | await ky.post('/tasks/' + id, {
75 | json: { id: id, completed: completed }
76 | }).json();
77 | store({
78 | tasks: store().tasks.map(t => (t.id === id ? { ...t, completed } : t))
79 | })
80 | })()
81 | },
82 | id => {
83 | (async () => {
84 | await ky.delete('/tasks/' + id, {
85 | json: { id: id }
86 | }).json();
87 | store({ tasks: store().tasks.filter(t => t.id !== id) })
88 | })()
89 | }
90 | )}
91 |
92 | `
93 |
94 | const renderApp = () => render(App(), document.body)
95 |
96 | const createStore = (initialState) => {
97 | let data = initialState
98 |
99 | return (update) => {
100 | if (update) {
101 | data = { ...data, ...update }
102 | renderApp()
103 | }
104 | return data
105 | }
106 | }
107 |
108 | const store = createStore({
109 | tasks: await ky.get('/tasks').json(),
110 | selectedTasks: [],
111 | inputText: "",
112 | })
113 |
114 | renderApp()
115 |
--------------------------------------------------------------------------------
/assets/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/assets/style.css:
--------------------------------------------------------------------------------
1 | .task.done .title {
2 | text-decoration: line-through;
3 | opacity: 0.3;
4 | }
5 |
6 | .task {
7 | display: flex;
8 | align-items: center;
9 | padding: 1em 0;
10 | }
11 |
12 | .task .title {
13 | flex-grow: 1;
14 | }
15 |
16 | .task-list {
17 | border-top: 1px solid #2f49770d;
18 | min-height: 13em;
19 | }
20 |
21 | body {
22 | font-family: "system-ui";
23 | }
24 |
25 | .task input[type="checkbox"] {
26 | margin-right: 1.5em;
27 | }
28 |
29 | .task .remove {
30 | font-size: 1.5em;
31 | font-weight: 100;
32 | cursor: pointer;
33 | opacity: 0.3;
34 | }
35 |
36 | body {
37 | background: white;
38 | color: #4f5375;
39 | width: 28em;
40 | margin: 3em auto;
41 | box-shadow: 0 2em 5em #132c531a;
42 | border-radius: 0.5em;
43 | padding: 1em 1.5em;
44 | }
45 |
46 | html {
47 | background: #f8f8f8;
48 | }
49 |
50 | .task {
51 | animation: 0.6s cubic-bezier(0.43, 0.51, 0.21, 0.97) 0s taskAppear;
52 | }
53 | @keyframes taskAppear {
54 | 0% {
55 | transform: translateX(-30%);
56 | opacity: 0;
57 | }
58 | 100% {
59 | transform: translateX(0);
60 | opacity: 1;
61 | }
62 | }
63 |
64 | .new-task {
65 | display: flex;
66 | }
67 |
68 | .new-task input[type="text"] {
69 | flex-grow: 1;
70 | margin: 2em 0;
71 | font-size: inherit;
72 | font-family: inherit;
73 | padding: 0.5em 0.7em;
74 | outline-width: 1px;
75 | border: 1px solid #0000001a;
76 | }
77 |
78 | .new-task input[type="text"]::-webkit-input-placeholder {
79 | color: #5a5d8038;
80 | }
81 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/mattn/go-prisma-example
2 |
3 | go 1.22
4 |
5 | require (
6 | github.com/joho/godotenv v1.5.1
7 | github.com/labstack/echo/v4 v4.12.0
8 | github.com/shopspring/decimal v1.4.0
9 | github.com/steebchen/prisma-client-go v0.36.0
10 | )
11 |
12 | require (
13 | github.com/labstack/gommon v0.4.2 // indirect
14 | github.com/mattn/go-colorable v0.1.13 // indirect
15 | github.com/mattn/go-isatty v0.0.20 // indirect
16 | github.com/valyala/bytebufferpool v1.0.0 // indirect
17 | github.com/valyala/fasttemplate v1.2.2 // indirect
18 | golang.org/x/crypto v0.22.0 // indirect
19 | golang.org/x/net v0.24.0 // indirect
20 | golang.org/x/sys v0.19.0 // indirect
21 | golang.org/x/text v0.14.0 // indirect
22 | )
23 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3 | github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
4 | github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
5 | github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0=
6 | github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM=
7 | github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
8 | github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
9 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
10 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
11 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
12 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
13 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
14 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
15 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
16 | github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
17 | github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
18 | github.com/steebchen/prisma-client-go v0.36.0 h1:jy0S4Eqme4oHQS/ZCqXI2vYPEwW4eJRV/3bSA0BN5C0=
19 | github.com/steebchen/prisma-client-go v0.36.0/go.mod h1:ImB+P8NBQ3zlY6MgGC/+jrtntXbj1UXwRfWrHQwNKEo=
20 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
21 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
22 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
23 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
24 | github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
25 | github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
26 | golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
27 | golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
28 | golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
29 | golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
30 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
31 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
32 | golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
33 | golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
34 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
35 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
36 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
37 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
38 |
--------------------------------------------------------------------------------
/k8s/knative.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: serving.knative.dev/v1
2 | kind: Service
3 | metadata:
4 | name: todoapp
5 | spec:
6 | template:
7 | metadata:
8 | labels:
9 | app: todoapp
10 | spec:
11 | containers:
12 | - name: todoapp
13 | image: ghcr.io/mattn/go-prisma-example
14 | imagePullPolicy: Always
15 | env:
16 | - name: DATABASE_URL
17 | valueFrom:
18 | secretKeyRef:
19 | name: todoapp
20 | key: database-url
21 | ports:
22 | - containerPort: 8989
23 | protocol: TCP
24 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | //go:generate npx prisma generate
4 |
5 | import (
6 | "context"
7 | "embed"
8 | "io/fs"
9 | "log"
10 | "mime"
11 | "net/http"
12 | "strconv"
13 |
14 | "github.com/labstack/echo/v4"
15 | "github.com/mattn/go-prisma-example/prisma/db"
16 | )
17 |
18 | const name = "go-prisma-example"
19 |
20 | const version = "0.0.4"
21 |
22 | var revision = "HEAD"
23 |
24 | //go:embed assets
25 | var assets embed.FS
26 |
27 | func main() {
28 | client := db.NewClient()
29 | if err := client.Prisma.Connect(); err != nil {
30 | log.Fatal(err)
31 | }
32 |
33 | defer func() {
34 | if err := client.Prisma.Disconnect(); err != nil {
35 | log.Fatal(err)
36 | }
37 | }()
38 |
39 | mime.AddExtensionType(".js", "application/javascript")
40 |
41 | e := echo.New()
42 |
43 | e.POST("/tasks", func(c echo.Context) error {
44 | var task db.TaskModel
45 | if err := c.Bind(&task); err != nil {
46 | c.Logger().Error("Bind: ", err)
47 | return c.String(http.StatusBadRequest, "Bind: "+err.Error())
48 | }
49 | var text *string
50 | if newText, ok := task.Text(); ok {
51 | text = &newText
52 | }
53 | var completed *bool
54 | if newCompleted, ok := task.Completed(); ok {
55 | completed = &newCompleted
56 | }
57 | newTask, err := client.Task.CreateOne(
58 | db.Task.Text.SetIfPresent(text),
59 | db.Task.Completed.SetIfPresent(completed),
60 | ).Exec(context.Background())
61 | if err != nil {
62 | return c.String(http.StatusBadRequest, err.Error())
63 | }
64 | return c.JSON(http.StatusOK, newTask)
65 | })
66 |
67 | e.GET("/tasks", func(c echo.Context) error {
68 | tasks, err := client.Task.FindMany().OrderBy(
69 | db.Task.ID.Order(db.ASC),
70 | ).Exec(context.Background())
71 | if err != nil {
72 | return c.String(http.StatusBadRequest, err.Error())
73 | }
74 | return c.JSON(http.StatusOK, tasks)
75 | })
76 |
77 | e.POST("/tasks/:id", func(c echo.Context) error {
78 | var task db.TaskModel
79 | if err := c.Bind(&task); err != nil {
80 | c.Logger().Error("Bind: ", err)
81 | return c.String(http.StatusBadRequest, "Bind: "+err.Error())
82 | }
83 | var text *string
84 | if newText, ok := task.Text(); ok {
85 | text = &newText
86 | }
87 | var completed *bool
88 | if newCompleted, ok := task.Completed(); ok {
89 | completed = &newCompleted
90 | }
91 | newTask, err := client.Task.FindUnique(
92 | db.Task.ID.Equals(task.ID),
93 | ).Update(
94 | db.Task.Text.SetIfPresent(text),
95 | db.Task.Completed.SetIfPresent(completed),
96 | ).Exec(context.Background())
97 | if err != nil {
98 | return c.String(http.StatusBadRequest, err.Error())
99 | }
100 | return c.JSON(http.StatusOK, newTask)
101 | })
102 |
103 | e.DELETE("/tasks/:id", func(c echo.Context) error {
104 | id, err := strconv.Atoi(c.Param("id"))
105 | if err != nil {
106 | return c.String(http.StatusBadRequest, err.Error())
107 | }
108 | task, err := client.Task.FindUnique(
109 | db.Task.ID.Equals(id),
110 | ).Delete().Exec(context.Background())
111 | if err != nil {
112 | return c.String(http.StatusNotFound, err.Error())
113 | }
114 | return c.JSON(http.StatusOK, task)
115 | })
116 | e.GET("/tasks/:id", func(c echo.Context) error {
117 | id, err := strconv.Atoi(c.Param("id"))
118 | if err != nil {
119 | return c.String(http.StatusBadRequest, err.Error())
120 | }
121 | task, err := client.Task.FindUnique(
122 | db.Task.ID.Equals(id),
123 | ).Exec(context.Background())
124 | if err != nil {
125 | return c.String(http.StatusNotFound, err.Error())
126 | }
127 | return c.JSON(http.StatusOK, task)
128 | })
129 |
130 | sub, _ := fs.Sub(assets, "assets")
131 | e.GET("/*", echo.WrapHandler(http.FileServer(http.FS(sub))))
132 | e.Logger.Fatal(e.Start(":8989"))
133 | }
134 |
--------------------------------------------------------------------------------
/prisma/db/.gitignore:
--------------------------------------------------------------------------------
1 | # gitignore generated by Prisma Client Go. DO NOT EDIT.
2 | *_gen.go
3 |
--------------------------------------------------------------------------------
/prisma/schema.prisma:
--------------------------------------------------------------------------------
1 | // This is your Prisma schema file,
2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema
3 |
4 | datasource db {
5 | provider = "postgresql"
6 | url = env("DATABASE_URL")
7 | }
8 |
9 | generator client {
10 | provider = "go run github.com/steebchen/prisma-client-go"
11 | }
12 |
13 | model Task {
14 | id Int @id @default(autoincrement())
15 | text String?
16 | completed Boolean? @default(false)
17 | }
18 |
--------------------------------------------------------------------------------