-
Notifications
You must be signed in to change notification settings - Fork 3
Open
Description
So i was really curious with this go ssr approach and went to try it but there was several issues.
- I had problems running docker container, running on M1 MBP and this dockerfile worked for me
FROM node:18-alpine as frontend
RUN npm install -g [email protected]
WORKDIR /app
COPY client ./client/
WORKDIR /app/client
RUN pnpm install --frozen-lockfile && pnpm build
FROM --platform=linux/amd64 ubuntu:latest as backend
# RUN apt update
ARG GO_VERSION
ENV GO_VERSION=${GO_VERSION}
RUN dpkg --add-architecture amd64
RUN apt-get update && apt-get upgrade
RUN apt-get install -y wget git gcc libc-dev make build-essential g++-x86-64-linux-gnu libc6-dev-amd64-cross gcc-aarch64-linux-gnu binutils:amd64 libc6-amd64-cross libstdc++6-amd64-cross
RUN ln -s /usr/x86_64-linux-gnu/lib64/ /lib64
ENV LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/lib64:/usr/x86_64-linux-gnu/lib"
# RUN apt install gcc libc-dev make build-base
RUN wget -P /tmp "https://dl.google.com/go/go1.22.2.linux-amd64.tar.gz"
RUN tar -C /usr/local -xzf "/tmp/go1.22.2.linux-amd64.tar.gz"
RUN rm "/tmp/go1.22.2.linux-amd64.tar.gz"
ENV GOPATH /go
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH"
WORKDIR /app
COPY --from=frontend /app/dist ./dist
COPY pkg/ ./pkg/
COPY go.mod go.sum Makefile main.go ./
RUN go mod download
RUN make build
FROM --platform=linux/amd64 ubuntu:latest as final
WORKDIR /app
COPY --from=backend /app/build/ /app/
EXPOSE 8080
CMD ["/app/server"]
And Makefile, not sure if it did anything but it is what it is
build:
# build the backend
env GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o $(BUILD_DIR)/$(APP_NAME) main.go
- As title says server returns a Promise in a string so for that i had to find a way to wait for it, on v8 repo in issues i found this posted
func resolvePromise(ctx *v8go.Context, val *v8go.Value, err error) (*v8go.Value, error) {
if err != nil || !val.IsPromise() {
return val, err
}
for {
switch p, _ := val.AsPromise(); p.State() {
case v8go.Fulfilled:
return p.Result(), nil
case v8go.Rejected:
return nil, errors.New(p.Result().DetailString())
case v8go.Pending:
ctx.PerformMicrotaskCheckpoint() // run VM to make progress on the promise
// go round the loop again...
default:
return nil, fmt.Errorf("illegal v8go.Promise state %d", p) // unreachable
}
}
}
And in a nutshell what we need to do is this
func (r *Renderer) Render(urlPath string) (string, error) {
iso := r.pool.Get()
defer r.pool.Put(iso)
ctx := v8go.NewContext(iso.Isolate)
defer ctx.Close()
iso.RenderScript.Run(ctx)
renderCmd := fmt.Sprintf(`ssrRender("%s")`, urlPath)
val, err := ctx.RunScript(renderCmd, r.ssrScriptName)
result, _ := resolvePromise(ctx, val, err)
if err != nil {
if jsErr, ok := err.(*v8go.JSError); ok {
err = fmt.Errorf("%v", jsErr.StackTrace)
}
return "", nil
}
return result.String(), nil
}
Maybe for the future there should be a check if a value of a script is a Promise then we wait for it otherwise just return a string.
I'm not sure if you want to add this edits but for people who will come upon this might help
revenkroz
Metadata
Metadata
Assignees
Labels
No labels