Skip to content

Commit 8655cee

Browse files
soneda-yuyaCopilot
andauthored
fix(server): story publishing isn't working (#1812)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent f567b91 commit 8655cee

File tree

7 files changed

+73
-25
lines changed

7 files changed

+73
-25
lines changed

server/.env.example

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ PORT=8080
33
REEARTH_DB=mongodb://localhost # for reearth database url
44
REEARTH_DB_ACCOUNT=reearth_account # for reearth account database name
55
REEARTH_DB_VIS=reearth # for reearth visualizer database name
6-
REEARTH_HOST=https://localhost:8080
7-
REEARTH_HOST_WEB=https://localhost:3000
8-
REEARTH_ASSETBASEURL=https://localhost:8080/assets
6+
REEARTH_HOST=http://localhost:8080
7+
REEARTH_HOST_WEB=http://localhost:3000
8+
REEARTH_ASSETBASEURL=http://localhost:8080/assets
99
REEARTH_DEV=false
1010

1111
# GCP
@@ -82,6 +82,8 @@ REEARTH_SES_NAME=
8282
#REEARTH_S3_PUBLICATIONCACHECONTROL=
8383

8484
# Storage Google GCS
85+
# if you want to use fake gcs on local, please set true on REEARTH_USE_FAKE_GCS
86+
#REEARTH_GCS_ISFAKE=false
8587
#REEARTH_GCS_BUCKETNAME=bucket_name
8688
#REEARTH_GCS_PUBLICATIONCACHECONTROL=
8789

@@ -110,3 +112,9 @@ REEARTH_VISUALIZER_POLICY_CHECKER_TOKEN=''
110112
# "http": Uses external HTTP service for policy validation (requires Endpoint)
111113
# "permissive" (default): Allows all operations without restrictions (OSS mode)
112114
REEARTH_VISUALIZER_POLICY_CHECKER_TYPE=permissive
115+
116+
# CORS config
117+
# Comma-separated list of allowed origins for CORS. Use '*' to allow all origins.
118+
#REEARTH_VISUALIZER_DOMAINCHECHECKER_TYPE=http
119+
#REEARTH_VISUALIZER_DOMAINCHECHECKER_ENDURL=http://localhost/api/internal/domain
120+
#REEARTH_VISUALIZER_DOMAINCHECHECKER_TOKEN=token

server/internal/app/config/file.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package config
22

33
type GCSConfig struct {
4+
IsFake bool `pp:",omitempty"`
45
BucketName string `pp:",omitempty"`
56
PublicationCacheControl string `pp:",omitempty"`
67
}

server/internal/app/internal/middleware/assets_cors_middleware.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package middleware
22

33
import (
4-
"net/http"
54
"net/url"
65
"strings"
76

@@ -14,9 +13,7 @@ func AssetsCORSMiddleware(domainChecker gateway.DomainChecker, allowedOrigins []
1413
return func(next echo.HandlerFunc) echo.HandlerFunc {
1514
return func(c echo.Context) error {
1615
origin := c.Request().Header.Get("Origin")
17-
if origin == "" {
18-
return c.NoContent(http.StatusBadRequest)
19-
}
16+
2017
allowedOrigin := ""
2118
for _, allowed := range allowedOrigins {
2219
if allowed == origin {

server/internal/app/internal/middleware/assets_cors_middleware_test.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func (m *mockDomainChecker) CheckDomain(ctx context.Context, req gateway.DomainC
2424
}
2525

2626
func TestAssetsCORSMiddleware(t *testing.T) {
27-
t.Run("no origin header returns bad request", func(t *testing.T) {
27+
t.Run("no origin header continues without CORS headers", func(t *testing.T) {
2828
e := echo.New()
2929
req := httptest.NewRequest("GET", "/", nil)
3030
rec := httptest.NewRecorder()
@@ -40,7 +40,11 @@ func TestAssetsCORSMiddleware(t *testing.T) {
4040

4141
err := h(c)
4242
assert.NoError(t, err)
43-
assert.Equal(t, http.StatusBadRequest, rec.Code)
43+
assert.Equal(t, http.StatusOK, rec.Code)
44+
assert.Empty(t, rec.Header().Get("Access-Control-Allow-Origin"))
45+
assert.Empty(t, rec.Header().Get("Access-Control-Allow-Methods"))
46+
assert.Empty(t, rec.Header().Get("Access-Control-Allow-Headers"))
47+
assert.Empty(t, rec.Header().Get("Access-Control-Max-Age"))
4448
})
4549

4650
t.Run("origin in allowed list", func(t *testing.T) {
@@ -184,7 +188,7 @@ func TestAssetsCORSMiddleware(t *testing.T) {
184188
assert.Equal(t, "86400", rec.Header().Get("Access-Control-Max-Age"))
185189
})
186190

187-
t.Run("returns bad request when no origin header", func(t *testing.T) {
191+
t.Run("continues without CORS headers when no origin header", func(t *testing.T) {
188192
e := echo.New()
189193
req := httptest.NewRequest("OPTIONS", "/", nil)
190194
rec := httptest.NewRecorder()
@@ -200,7 +204,11 @@ func TestAssetsCORSMiddleware(t *testing.T) {
200204

201205
err := h(c)
202206
assert.NoError(t, err)
203-
assert.Equal(t, http.StatusBadRequest, rec.Code)
207+
assert.Equal(t, http.StatusOK, rec.Code)
208+
assert.Empty(t, rec.Header().Get("Access-Control-Allow-Origin"))
209+
assert.Empty(t, rec.Header().Get("Access-Control-Allow-Methods"))
210+
assert.Empty(t, rec.Header().Get("Access-Control-Allow-Headers"))
211+
assert.Empty(t, rec.Header().Get("Access-Control-Max-Age"))
204212
})
205213
})
206214

server/internal/app/repo.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,8 @@ func initReposAndGateways(ctx context.Context, conf *config.Config, debug bool)
154154
func initFile(ctx context.Context, conf *config.Config) (fileRepo gateway.File) {
155155
var err error
156156
if conf.GCS.IsConfigured() {
157-
log.Infofc(ctx, "file: GCS storage is used: %s\n", conf.GCS.BucketName)
158-
log.Infofc(ctx, "file: GCS storage base URL: %s\n", conf.AssetBaseURL)
159-
fileRepo, err = gcs.NewFile(false, conf.GCS.BucketName, conf.AssetBaseURL, conf.GCS.PublicationCacheControl)
157+
isFake := conf.GCS.IsFake
158+
fileRepo, err = gcs.NewFile(isFake, conf.GCS.BucketName, conf.AssetBaseURL, conf.GCS.PublicationCacheControl)
160159
if err != nil {
161160
log.Warnf("file: failed to init GCS storage: %s\n", err.Error())
162161
}

server/internal/infrastructure/gcs/file.go

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/reearth/reearth/server/internal/usecase/gateway"
1919
"github.com/reearth/reearth/server/pkg/file"
2020
"github.com/reearth/reearth/server/pkg/id"
21+
"github.com/reearth/reearth/server/pkg/visualizer"
2122
"github.com/reearth/reearthx/log"
2223
"github.com/reearth/reearthx/rerror"
2324
"github.com/spf13/afero"
@@ -33,14 +34,14 @@ const (
3334
)
3435

3536
type fileRepo struct {
36-
isTest bool
37+
isFake bool
3738
bucketName string
3839
base *url.URL
3940
cacheControl string
4041
baseFileStorage *infrastructure.BaseFileStorage
4142
}
4243

43-
func NewFile(isTest bool, bucketName, base string, cacheControl string) (gateway.File, error) {
44+
func NewFile(isFake bool, bucketName, base string, cacheControl string) (gateway.File, error) {
4445
if bucketName == "" {
4546
return nil, errors.New("bucket name is empty")
4647
}
@@ -57,7 +58,7 @@ func NewFile(isTest bool, bucketName, base string, cacheControl string) (gateway
5758
}
5859

5960
return &fileRepo{
60-
isTest: isTest,
61+
isFake: isFake,
6162
bucketName: bucketName,
6263
base: u,
6364
cacheControl: cacheControl,
@@ -301,7 +302,7 @@ func (f *fileRepo) bucket(ctx context.Context) (*storage.BucketHandle, error) {
301302
var client *storage.Client
302303
var err error
303304

304-
if f.isTest {
305+
if f.isFake {
305306
testGCS, err := testutil.NewGCSForTesting()
306307
if err != nil {
307308
return nil, err
@@ -326,22 +327,53 @@ func (f *fileRepo) bucket(ctx context.Context) (*storage.BucketHandle, error) {
326327

327328
func (f *fileRepo) read(ctx context.Context, filename string) (io.ReadCloser, error) {
328329
if filename == "" {
330+
visualizer.WarnWithCallerLogging(ctx, "gcs: read filename is empty")
329331
return nil, rerror.ErrNotFound
330332
}
331333

332334
bucket, err := f.bucket(ctx)
333335
if err != nil {
334-
log.Errorfc(ctx, "gcs: read bucket err: %+v\n", err)
335-
return nil, rerror.ErrInternalByWithContext(ctx, err)
336+
return nil, visualizer.ErrorWithCallerLogging(ctx, "gcs: read bucket err", rerror.ErrInternalByWithContext(ctx, err))
337+
}
338+
339+
_, err = bucket.Object(filename).Attrs(ctx)
340+
if err != nil && errors.Is(err, storage.ErrObjectNotExist) {
341+
visualizer.WarnWithCallerLogging(ctx, "gcs: read attrs err")
342+
return nil, rerror.ErrNotFound
343+
}
344+
345+
if err != nil {
346+
return nil, visualizer.ErrorWithCallerLogging(ctx, "gcs: read attrs err", rerror.ErrInternalByWithContext(ctx, err))
347+
}
348+
349+
// Note:
350+
// fsouza/fake-gcs-server can't read object by Reader.
351+
// so we need to download it from the server directly.
352+
if f.isFake {
353+
u := fmt.Sprintf("http://%s/download/storage/v1/b/%s/o/%s?alt=media",
354+
strings.TrimRight("localhost:4443", "/"),
355+
url.PathEscape(bucket.BucketName()),
356+
url.PathEscape(filename),
357+
)
358+
359+
resp, err := http.Get(u)
360+
if err != nil {
361+
return nil, err
362+
}
363+
364+
if resp.StatusCode != http.StatusOK {
365+
body, _ := io.ReadAll(resp.Body)
366+
return nil, visualizer.ErrorWithCallerLogging(ctx, "gcs: read fake object err", fmt.Errorf("emu GET failed: status=%d body=%s", resp.StatusCode, string(body)))
367+
}
368+
return resp.Body, nil
336369
}
337370

338371
reader, err := bucket.Object(filename).NewReader(ctx)
339372
if err != nil {
340373
if errors.Is(err, storage.ErrObjectNotExist) {
341374
return nil, rerror.ErrNotFound
342375
}
343-
log.Errorfc(ctx, "gcs: read err: %+v\n", err)
344-
return nil, rerror.ErrInternalByWithContext(ctx, err)
376+
return nil, visualizer.ErrorWithCallerLogging(ctx, "gcs: read object err", rerror.ErrInternalByWithContext(ctx, err))
345377
}
346378

347379
return reader, nil

server/internal/usecase/interactor/published.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/reearth/reearth/server/internal/usecase/gateway"
1717
"github.com/reearth/reearth/server/internal/usecase/interfaces"
1818
"github.com/reearth/reearth/server/internal/usecase/repo"
19+
"github.com/reearth/reearth/server/pkg/visualizer"
1920
"github.com/reearth/reearthx/log"
2021
"github.com/reearth/reearthx/rerror"
2122
"github.com/reearth/reearthx/util"
@@ -73,6 +74,7 @@ func NewPublishedWithURL(project repo.Project, storytelling repo.Storytelling, f
7374
func (i *Published) Metadata(ctx context.Context, name string) (interfaces.PublishedMetadata, error) {
7475
prj, err := i.project.FindByPublicName(ctx, name)
7576
if err != nil && !errors.Is(err, rerror.ErrNotFound) {
77+
log.Warnfc(ctx, "published metadata: find by public name err: %s", err)
7678
return interfaces.PublishedMetadata{}, err
7779
}
7880

@@ -93,20 +95,21 @@ func (i *Published) Metadata(ctx context.Context, name string) (interfaces.Publi
9395
func (i *Published) Data(ctx context.Context, name string) (io.Reader, error) {
9496
r, err := i.file.ReadBuiltSceneFile(ctx, name)
9597
if err != nil && err != rerror.ErrNotFound {
96-
return nil, err
98+
return nil, visualizer.ErrorWithCallerLogging(ctx, "published: read built scene file", err)
9799
}
98100
if r != nil {
99101
return r, nil
100102
}
101103

102104
r, err = i.file.ReadStoryFile(ctx, name)
103105
if err != nil && err != rerror.ErrNotFound {
104-
return nil, err
106+
return nil, visualizer.ErrorWithCallerLogging(ctx, "published: read story file", err)
105107
}
106108
if r != nil {
107109
return r, nil
108110
}
109-
return nil, rerror.ErrNotFound
111+
112+
return nil, visualizer.ErrorWithCallerLogging(ctx, "published: no data file found", rerror.ErrNotFound)
110113
}
111114

112115
func (i *Published) Index(ctx context.Context, name string, u *url.URL) (string, error) {

0 commit comments

Comments
 (0)