ОБТ
This commit is contained in:
parent
a1dc96088c
commit
1ad012b8fa
14 changed files with 136 additions and 52 deletions
19
Caddyfile
Normal file
19
Caddyfile
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
debug
|
||||
}
|
||||
|
||||
http:// {
|
||||
handle_path /file/* {
|
||||
root * /app/store
|
||||
file_server
|
||||
}
|
||||
handle /api* {
|
||||
reverse_proxy http://app:8000
|
||||
}
|
||||
handle {
|
||||
root * /app/frontend/dist
|
||||
file_server
|
||||
try_files {path} /index.html
|
||||
encode gzip zstd
|
||||
}
|
||||
}
|
|
@ -377,6 +377,19 @@ paths:
|
|||
responses:
|
||||
200:
|
||||
$ref: '#/components/responses/taskResponse'
|
||||
/file/{uid}:
|
||||
get:
|
||||
operationId: getFile
|
||||
parameters:
|
||||
- in: path
|
||||
name: uid
|
||||
required: true
|
||||
schema:
|
||||
format: uuid
|
||||
type: string
|
||||
response:
|
||||
307:
|
||||
description: redirect
|
||||
/games:
|
||||
get:
|
||||
responses:
|
||||
|
|
|
@ -40,4 +40,17 @@ paths:
|
|||
responses:
|
||||
200:
|
||||
$ref: "#/components/responses/taskResponse"
|
||||
/file/{uid}:
|
||||
get:
|
||||
operationId: getFile
|
||||
parameters:
|
||||
- name: uid
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
response:
|
||||
307:
|
||||
description: redirect
|
||||
|
||||
|
|
|
@ -43,6 +43,9 @@ type ServerInterface interface {
|
|||
// (POST /engine/{uid}/code)
|
||||
EnterCode(ctx echo.Context, uid openapi_types.UUID) error
|
||||
|
||||
// (GET /file/{uid})
|
||||
GetFile(ctx echo.Context, uid openapi_types.UUID) error
|
||||
|
||||
// (GET /games)
|
||||
GetGames(ctx echo.Context) error
|
||||
|
||||
|
@ -176,6 +179,24 @@ func (w *ServerInterfaceWrapper) EnterCode(ctx echo.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// GetFile converts echo context to params.
|
||||
func (w *ServerInterfaceWrapper) GetFile(ctx echo.Context) error {
|
||||
var err error
|
||||
// ------------- Path parameter "uid" -------------
|
||||
var uid openapi_types.UUID
|
||||
|
||||
err = runtime.BindStyledParameterWithOptions("simple", "uid", ctx.Param("uid"), &uid, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter uid: %s", err))
|
||||
}
|
||||
|
||||
ctx.Set(CookieAuthScopes, []string{})
|
||||
|
||||
// Invoke the callback with all the unmarshaled arguments
|
||||
err = w.Handler.GetFile(ctx, uid)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetGames converts echo context to params.
|
||||
func (w *ServerInterfaceWrapper) GetGames(ctx echo.Context) error {
|
||||
var err error
|
||||
|
@ -262,6 +283,7 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
|
|||
router.GET(baseURL+"/admin/games/:uid", wrapper.AdminGetGame)
|
||||
router.GET(baseURL+"/engine/:uid", wrapper.GameEngine)
|
||||
router.POST(baseURL+"/engine/:uid/code", wrapper.EnterCode)
|
||||
router.GET(baseURL+"/file/:uid", wrapper.GetFile)
|
||||
router.GET(baseURL+"/games", wrapper.GetGames)
|
||||
router.GET(baseURL+"/user", wrapper.GetUser)
|
||||
router.POST(baseURL+"/user/login", wrapper.PostUserLogin)
|
||||
|
@ -273,26 +295,27 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
|
|||
// Base64 encoded, gzipped, json marshaled Swagger object
|
||||
var swaggerSpec = []string{
|
||||
|
||||
"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",
|
||||
"0I910y50yiIW0sJWrst/AwAA//8rjKvDLRwAAA==",
|
||||
}
|
||||
|
||||
// GetSwagger returns the content of the embedded swagger specification file
|
||||
|
|
|
@ -14,7 +14,6 @@ RUN apk update --no-cache && apk add --no-cache tzdata
|
|||
COPY go.mod go.sum ./
|
||||
RUN go mod download && go mod verify
|
||||
COPY . .
|
||||
COPY --from=frontend /app/dist /app/frontend/dist
|
||||
RUN go build -ldflags="-X gitrepo.ru/neonxp/nquest.Version=${VERSION}" -o nquest *.go
|
||||
|
||||
# Runtime container
|
||||
|
@ -24,6 +23,7 @@ RUN apk update --no-cache && apk add --no-cache ca-certificates
|
|||
|
||||
COPY --from=backend /usr/share/zoneinfo/Europe/Moscow /usr/share/zoneinfo/Europe/Moscow
|
||||
COPY --from=backend /app/nquest /app/nquest
|
||||
COPY --from=frontend /app/dist /app/frontend/dist
|
||||
|
||||
ENV TZ Europe/Moscow
|
||||
|
3
build/caddy/Dockerfile
Normal file
3
build/caddy/Dockerfile
Normal file
|
@ -0,0 +1,3 @@
|
|||
FROM caddy:2.8-alpine
|
||||
|
||||
COPY Caddyfile /etc/caddy/Caddyfile
|
|
@ -3,19 +3,30 @@ version: '3.8'
|
|||
volumes:
|
||||
postgres-data:
|
||||
storage:
|
||||
frontend:
|
||||
|
||||
services:
|
||||
caddy:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: build/caddy/Dockerfile
|
||||
volumes:
|
||||
- frontend:/app/frontend/dist:ro
|
||||
- storage:/app/store:ro
|
||||
ports:
|
||||
- 8989:80
|
||||
depends_on:
|
||||
- app
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- 8989:8989
|
||||
dockerfile: build/api/Dockerfile
|
||||
depends_on:
|
||||
- db
|
||||
env_file:
|
||||
- .env
|
||||
volumes:
|
||||
- frontend:/app/frontend/dist:rw
|
||||
- storage:/app/store:rw
|
||||
db:
|
||||
image: postgres:15-alpine3.17
|
||||
|
|
17
embeds.go
17
embeds.go
|
@ -1,17 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed all:frontend/dist
|
||||
dist embed.FS
|
||||
//go:embed frontend/dist/index.html
|
||||
indexHTML embed.FS
|
||||
|
||||
distDirFS = echo.MustSubFS(dist, "frontend/dist")
|
||||
distIndexHtml = echo.MustSubFS(indexHTML, "frontend/dist")
|
||||
)
|
|
@ -4,7 +4,7 @@ import { UserProvider } from '../store/user'
|
|||
import { ajax } from '../utils/fetch'
|
||||
import { Layout, Menu } from 'antd'
|
||||
import { MenuOutlined } from '@ant-design/icons'
|
||||
import { Content, Header } from 'antd/es/layout/layout'
|
||||
import { Content, Footer, Header } from 'antd/es/layout/layout'
|
||||
import { useRole } from '../utils/roles'
|
||||
|
||||
const AppLayout = () => {
|
||||
|
@ -80,9 +80,12 @@ const AppLayout = () => {
|
|||
style={{ width: '100%' }}
|
||||
/>
|
||||
</Header>
|
||||
<Content style={{ padding: '0 24px' }}>
|
||||
<Content style={{ padding: '0 24px', minHeight: '80vh' }}>
|
||||
<Outlet />
|
||||
</Content>
|
||||
<Footer>
|
||||
Сделал <a href="https://neonxp.ru">NeonXP</a> в 2024.
|
||||
</Footer>
|
||||
</Layout>)
|
||||
}
|
||||
|
||||
|
|
|
@ -8,12 +8,16 @@ const Index = () => {
|
|||
const { user } = UserProvider.useContainer()
|
||||
const navigate = useNavigate()
|
||||
return (<>
|
||||
<Title>NQuest</Title>
|
||||
<Title><img src="/assets/logo.png" />uest</Title>
|
||||
<Paragraph>Привет! Это платформа для ARG игр.</Paragraph>
|
||||
<Paragraph>
|
||||
Если ты попал сюда случайно, то скорее всего, для тебя здесь нет ничего интересного.
|
||||
Если ты попал сюда случайно, то скорее всего, для тебя здесь нет ничего интересного.<br />
|
||||
А если ты знаешь зачем пришёл, то добро пожаловать!
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
Телеграм-канал с новостями проекта: <a href="https://t.me/nquest_ru">nquest_ru</a>, рекомендую подписаться.<br />
|
||||
Там будут как roadmap проекта, так и анонсы новых квестов.
|
||||
</Paragraph>
|
||||
{!user
|
||||
? (<Button.Group>
|
||||
<Button type="primary" onClick={() => navigate('/login')}>Вход</Button>
|
||||
|
|
|
@ -38,16 +38,14 @@ const renderItem = (user, navigate, item) => {
|
|||
</Popover>,
|
||||
<Popover key='taskCount' content={`Этот квест состоит из ${item.taskCount} уровней`} title='Количество уровней в квесте'>
|
||||
<Space>{item.taskCount} ур</Space>
|
||||
</Popover>,
|
||||
<>{moment(item.createdAt).fromNow()}</>,
|
||||
<>Автор {item.authors.map(a => a.username)}</>
|
||||
</Popover>
|
||||
]
|
||||
|
||||
let questAction = (<span>Необходимо войти</span>)
|
||||
|
||||
if (user) {
|
||||
questAction = (user.games.find(x => x.id === item.id)
|
||||
? <span>Вы уже прошли этот квест</span>
|
||||
? <b>Вы уже прошли этот квест</b>
|
||||
: <Button onClick={() => navigate(`/go/${item.id}`)} type="primary">Начать квест</Button>
|
||||
)
|
||||
}
|
||||
|
|
3
main.go
3
main.go
|
@ -130,9 +130,6 @@ func main() {
|
|||
// --[ System ]--
|
||||
e.GET("/metrics", echoprometheus.NewHandler())
|
||||
|
||||
e.StaticFS("/file", afero.NewIOFS(storage))
|
||||
e.StaticFS("/*", distDirFS)
|
||||
|
||||
e.Logger.Debugf("backend version %s", Version)
|
||||
e.Logger.Fatal(e.Start(cfg.Listen))
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"gitrepo.ru/neonxp/nquest/api"
|
||||
|
@ -57,3 +59,12 @@ func (u *File) AdminListFiles(c echo.Context, quest uuid.UUID) error {
|
|||
|
||||
return c.JSON(200, list)
|
||||
}
|
||||
|
||||
func (u *File) GetFile(c echo.Context, uid uuid.UUID) error {
|
||||
f, err := u.FileService.GetFilesByID(c.Request().Context(), uid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Redirect(307, fmt.Sprintf(`/file/%s/%s`, f.QuestID, f.Filename))
|
||||
}
|
||||
|
|
|
@ -57,3 +57,9 @@ func (u *File) GetFilesByQuest(ctx context.Context, quest uuid.UUID) ([]*models.
|
|||
|
||||
return list, u.DB.WithContext(ctx).Find(&list, `quest_id = ?`, quest.String()).Error
|
||||
}
|
||||
|
||||
func (u *File) GetFilesByID(ctx context.Context, fileID uuid.UUID) (*models.File, error) {
|
||||
file := &models.File{}
|
||||
|
||||
return file, u.DB.WithContext(ctx).First(file, fileID).Error
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue