@sonatel-os/strapi-plugin-tcg 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +143 -2
- package/dist/_chunks/App-DvvgeCGs.js +5190 -0
- package/dist/_chunks/App-j9qu2-To.mjs +5188 -0
- package/dist/_chunks/SettingsPage-BcJXkWnW.mjs +191 -0
- package/dist/_chunks/SettingsPage-Bxa_71iT.js +191 -0
- package/dist/_chunks/api-Aney2S9e.js +1011 -0
- package/dist/_chunks/api-DACN35Y5.mjs +1012 -0
- package/dist/_chunks/en-B2j8rFto.js +39 -0
- package/dist/_chunks/en-C33hmbtk.mjs +39 -0
- package/dist/_chunks/fr-B2Toyhf9.js +37 -0
- package/dist/_chunks/fr-JWwsoAzl.mjs +37 -0
- package/dist/_chunks/index-DSm3eEtn.mjs +108 -0
- package/dist/_chunks/index-DXuaf32J.js +107 -0
- package/dist/admin/index.js +3 -0
- package/dist/admin/index.mjs +4 -0
- package/dist/server/index.js +4483 -0
- package/dist/server/index.mjs +4484 -0
- package/package.json +82 -4
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Grid, Box, Flex, Typography, Badge } from "@strapi/design-system";
|
|
3
|
+
import { Page, Layouts } from "@strapi/strapi/admin";
|
|
4
|
+
import { ai as useGetSettingsQuery, I as Intro, g as Section, Y as Mono } from "./api-DACN35Y5.mjs";
|
|
5
|
+
import { a as PERMISSIONS } from "./index-DSm3eEtn.mjs";
|
|
6
|
+
const Row = ({ k, v, hint, tone }) => /* @__PURE__ */ jsxs(
|
|
7
|
+
Flex,
|
|
8
|
+
{
|
|
9
|
+
justifyContent: "space-between",
|
|
10
|
+
alignItems: "flex-start",
|
|
11
|
+
gap: 4,
|
|
12
|
+
paddingTop: 3,
|
|
13
|
+
paddingBottom: 3,
|
|
14
|
+
style: { borderBottom: "1px solid #eaeaef" },
|
|
15
|
+
children: [
|
|
16
|
+
/* @__PURE__ */ jsxs(Box, { style: { flex: 1, minWidth: 0 }, children: [
|
|
17
|
+
/* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "semiBold", tag: "p", children: k }),
|
|
18
|
+
hint ? /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", tag: "p", children: hint }) : null
|
|
19
|
+
] }),
|
|
20
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", style: { flexShrink: 0 }, children: [
|
|
21
|
+
tone ? /* @__PURE__ */ jsx(Badge, { backgroundColor: `${tone}100`, textColor: `${tone}700`, size: "S", children: tone === "success" ? "set" : "default" }) : null,
|
|
22
|
+
/* @__PURE__ */ jsx(Mono, { variant: "pi", fontWeight: "semiBold", children: String(v ?? "—") || "—" })
|
|
23
|
+
] })
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
const SettingsPage = () => {
|
|
28
|
+
const { data, isLoading, isError } = useGetSettingsQuery();
|
|
29
|
+
if (isLoading) return /* @__PURE__ */ jsx(Page.Loading, {});
|
|
30
|
+
if (isError) return /* @__PURE__ */ jsx(Page.Error, {});
|
|
31
|
+
const s = data ?? {};
|
|
32
|
+
const players = s.players ?? {};
|
|
33
|
+
return /* @__PURE__ */ jsx(Page.Protect, { permissions: PERMISSIONS.settingsRead, children: /* @__PURE__ */ jsxs(Page.Main, { children: [
|
|
34
|
+
/* @__PURE__ */ jsx(Page.Title, { children: "TCG · Configuration" }),
|
|
35
|
+
/* @__PURE__ */ jsx(
|
|
36
|
+
Layouts.Header,
|
|
37
|
+
{
|
|
38
|
+
title: "TCG configuration",
|
|
39
|
+
subtitle: "Read from config/plugins.js and the server environment. Nothing here is edited in the admin; secrets are masked."
|
|
40
|
+
}
|
|
41
|
+
),
|
|
42
|
+
/* @__PURE__ */ jsxs(Layouts.Content, { children: [
|
|
43
|
+
/* @__PURE__ */ jsx(
|
|
44
|
+
Intro,
|
|
45
|
+
{
|
|
46
|
+
id: "settings",
|
|
47
|
+
title: "How a host app talks to the game",
|
|
48
|
+
body: [
|
|
49
|
+
"The plugin does not do authentication of its own. Its player routes under /api/tcg are ordinary Strapi content-API routes: give an API token (Settings → API Tokens) or a Users & Permissions role the TCG actions you want to allow, and call the routes with that credential. Any Strapi can install the plugin and any host can drive it.",
|
|
50
|
+
"The caller names the player with a header (x-player-id by default) carrying any stable id it owns: a phone number, a Max It user id, a username. Unknown ids create a player on first sight (autoProvision), and an optional name and avatar header refresh the profile. Catalog routes (season, packs, cards, live, rewards) need no player header.",
|
|
51
|
+
"Out of the box no configuration is needed. Before an event set TCG_QR_SECRET so printed and rotating QR codes survive a rotation of Strapi’s app keys."
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
),
|
|
55
|
+
/* @__PURE__ */ jsxs(Grid.Root, { gap: 4, children: [
|
|
56
|
+
/* @__PURE__ */ jsx(Grid.Item, { col: 6, s: 12, alignItems: "stretch", children: /* @__PURE__ */ jsxs(
|
|
57
|
+
Section,
|
|
58
|
+
{
|
|
59
|
+
eyebrow: "The contract",
|
|
60
|
+
title: "Player identity",
|
|
61
|
+
help: "Header names the caller must send on player routes.",
|
|
62
|
+
style: { width: "100%" },
|
|
63
|
+
children: [
|
|
64
|
+
/* @__PURE__ */ jsx(
|
|
65
|
+
Row,
|
|
66
|
+
{
|
|
67
|
+
k: "Player id header",
|
|
68
|
+
hint: "players.idHeader · required on every player route",
|
|
69
|
+
v: players.idHeader || "x-player-id",
|
|
70
|
+
tone: players.idHeader && players.idHeader !== "x-player-id" ? "success" : "warning"
|
|
71
|
+
}
|
|
72
|
+
),
|
|
73
|
+
/* @__PURE__ */ jsx(
|
|
74
|
+
Row,
|
|
75
|
+
{
|
|
76
|
+
k: "Display name header",
|
|
77
|
+
hint: "players.nameHeader · optional hint, refreshes the profile",
|
|
78
|
+
v: players.nameHeader || "x-player-name",
|
|
79
|
+
tone: players.nameHeader && players.nameHeader !== "x-player-name" ? "success" : "warning"
|
|
80
|
+
}
|
|
81
|
+
),
|
|
82
|
+
/* @__PURE__ */ jsx(
|
|
83
|
+
Row,
|
|
84
|
+
{
|
|
85
|
+
k: "Avatar header",
|
|
86
|
+
hint: "players.avatarHeader · optional URL",
|
|
87
|
+
v: players.avatarHeader || "x-player-avatar",
|
|
88
|
+
tone: players.avatarHeader && players.avatarHeader !== "x-player-avatar" ? "success" : "warning"
|
|
89
|
+
}
|
|
90
|
+
),
|
|
91
|
+
/* @__PURE__ */ jsx(
|
|
92
|
+
Row,
|
|
93
|
+
{
|
|
94
|
+
k: "Auto-provision",
|
|
95
|
+
hint: "players.autoProvision · create a player for an unknown id; off answers 404 PLAYER_NOT_FOUND",
|
|
96
|
+
v: players.autoProvision === false ? "off" : "on",
|
|
97
|
+
tone: players.autoProvision === false ? "success" : "warning"
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
) }),
|
|
103
|
+
/* @__PURE__ */ jsx(Grid.Item, { col: 6, s: 12, alignItems: "stretch", children: /* @__PURE__ */ jsxs(
|
|
104
|
+
Section,
|
|
105
|
+
{
|
|
106
|
+
eyebrow: "Before an event",
|
|
107
|
+
title: "Secrets and defaults",
|
|
108
|
+
help: "Shown with their effective value.",
|
|
109
|
+
style: { width: "100%" },
|
|
110
|
+
children: [
|
|
111
|
+
/* @__PURE__ */ jsx(
|
|
112
|
+
Row,
|
|
113
|
+
{
|
|
114
|
+
k: "QR global secret",
|
|
115
|
+
hint: "TCG_QR_SECRET · signs trade QR codes and packs without their own secret; derived from the app keys when unset",
|
|
116
|
+
v: s.qr?.globalSecret || "derived from app keys",
|
|
117
|
+
tone: s.qr?.globalSecret ? "success" : "warning"
|
|
118
|
+
}
|
|
119
|
+
),
|
|
120
|
+
/* @__PURE__ */ jsx(
|
|
121
|
+
Row,
|
|
122
|
+
{
|
|
123
|
+
k: "Player code prefix",
|
|
124
|
+
hint: "TCG_PLAYER_CODE_PREFIX · the JMB in JMB-A7K2",
|
|
125
|
+
v: s.playerCodePrefix || "TCG",
|
|
126
|
+
tone: s.playerCodePrefix && s.playerCodePrefix !== "TCG" ? "success" : "warning"
|
|
127
|
+
}
|
|
128
|
+
),
|
|
129
|
+
/* @__PURE__ */ jsx(
|
|
130
|
+
Row,
|
|
131
|
+
{
|
|
132
|
+
k: "Background sweeps",
|
|
133
|
+
hint: "Expired QR nonces and trade offers are cleaned up by the server",
|
|
134
|
+
v: s.sweeps?.enabled === false ? "off" : "on",
|
|
135
|
+
tone: "warning"
|
|
136
|
+
}
|
|
137
|
+
)
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
) })
|
|
141
|
+
] }),
|
|
142
|
+
/* @__PURE__ */ jsx(Box, { paddingTop: 6, children: /* @__PURE__ */ jsxs(Grid.Root, { gap: 4, children: [
|
|
143
|
+
/* @__PURE__ */ jsx(Grid.Item, { col: 6, s: 12, alignItems: "stretch", children: /* @__PURE__ */ jsx(
|
|
144
|
+
Section,
|
|
145
|
+
{
|
|
146
|
+
eyebrow: "Copy / paste",
|
|
147
|
+
title: "config/plugins.js",
|
|
148
|
+
help: "Everything is optional; values come from the environment.",
|
|
149
|
+
style: { width: "100%" },
|
|
150
|
+
children: /* @__PURE__ */ jsx(Mono, { variant: "pi", tag: "pre", style: { whiteSpace: "pre-wrap" }, children: `tcg: {
|
|
151
|
+
enabled: true,
|
|
152
|
+
config: {
|
|
153
|
+
playerCodePrefix: env('TCG_PLAYER_CODE_PREFIX', '${s.playerCodePrefix ?? "TCG"}'),
|
|
154
|
+
qr: { globalSecret: env('TCG_QR_SECRET', '') },
|
|
155
|
+
players: {
|
|
156
|
+
idHeader: '${players.idHeader ?? "x-player-id"}',
|
|
157
|
+
nameHeader: '${players.nameHeader ?? "x-player-name"}',
|
|
158
|
+
avatarHeader: '${players.avatarHeader ?? "x-player-avatar"}',
|
|
159
|
+
autoProvision: ${players.autoProvision === false ? "false" : "true"},
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
},` })
|
|
163
|
+
}
|
|
164
|
+
) }),
|
|
165
|
+
/* @__PURE__ */ jsx(Grid.Item, { col: 6, s: 12, alignItems: "stretch", children: /* @__PURE__ */ jsx(
|
|
166
|
+
Section,
|
|
167
|
+
{
|
|
168
|
+
eyebrow: "Copy / paste",
|
|
169
|
+
title: "A call from a host",
|
|
170
|
+
help: "An API token with the TCG actions, plus the player header.",
|
|
171
|
+
style: { width: "100%" },
|
|
172
|
+
children: /* @__PURE__ */ jsx(Mono, { variant: "pi", tag: "pre", style: { whiteSpace: "pre-wrap" }, children: `POST /api/tcg/pulls/scan
|
|
173
|
+
Authorization: Bearer <strapi api token>
|
|
174
|
+
${players.idHeader ?? "x-player-id"}: 221770000001
|
|
175
|
+
${players.nameHeader ?? "x-player-name"}: Awa Diop
|
|
176
|
+
Content-Type: application/json
|
|
177
|
+
|
|
178
|
+
{ "qrToken": "p.…" }
|
|
179
|
+
|
|
180
|
+
→ 201 { "data": { "card": { … }, "mintNumber": 17, "isNew": true } }
|
|
181
|
+
→ 400 { "error": { "code": "PLAYER_REQUIRED" } } // header missing
|
|
182
|
+
→ 403 { "error": { "code": "PLAYER_INACTIVE" } } // deactivated in Players` })
|
|
183
|
+
}
|
|
184
|
+
) })
|
|
185
|
+
] }) })
|
|
186
|
+
] })
|
|
187
|
+
] }) });
|
|
188
|
+
};
|
|
189
|
+
export {
|
|
190
|
+
SettingsPage
|
|
191
|
+
};
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const designSystem = require("@strapi/design-system");
|
|
5
|
+
const admin = require("@strapi/strapi/admin");
|
|
6
|
+
const api = require("./api-Aney2S9e.js");
|
|
7
|
+
const index = require("./index-DXuaf32J.js");
|
|
8
|
+
const Row = ({ k, v, hint, tone }) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9
|
+
designSystem.Flex,
|
|
10
|
+
{
|
|
11
|
+
justifyContent: "space-between",
|
|
12
|
+
alignItems: "flex-start",
|
|
13
|
+
gap: 4,
|
|
14
|
+
paddingTop: 3,
|
|
15
|
+
paddingBottom: 3,
|
|
16
|
+
style: { borderBottom: "1px solid #eaeaef" },
|
|
17
|
+
children: [
|
|
18
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { flex: 1, minWidth: 0 }, children: [
|
|
19
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "semiBold", tag: "p", children: k }),
|
|
20
|
+
hint ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", tag: "p", children: hint }) : null
|
|
21
|
+
] }),
|
|
22
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", style: { flexShrink: 0 }, children: [
|
|
23
|
+
tone ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: `${tone}100`, textColor: `${tone}700`, size: "S", children: tone === "success" ? "set" : "default" }) : null,
|
|
24
|
+
/* @__PURE__ */ jsxRuntime.jsx(api.Mono, { variant: "pi", fontWeight: "semiBold", children: String(v ?? "—") || "—" })
|
|
25
|
+
] })
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
);
|
|
29
|
+
const SettingsPage = () => {
|
|
30
|
+
const { data, isLoading, isError } = api.useGetSettingsQuery();
|
|
31
|
+
if (isLoading) return /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Loading, {});
|
|
32
|
+
if (isError) return /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Error, {});
|
|
33
|
+
const s = data ?? {};
|
|
34
|
+
const players = s.players ?? {};
|
|
35
|
+
return /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Protect, { permissions: index.PERMISSIONS.settingsRead, children: /* @__PURE__ */ jsxRuntime.jsxs(admin.Page.Main, { children: [
|
|
36
|
+
/* @__PURE__ */ jsxRuntime.jsx(admin.Page.Title, { children: "TCG · Configuration" }),
|
|
37
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
38
|
+
admin.Layouts.Header,
|
|
39
|
+
{
|
|
40
|
+
title: "TCG configuration",
|
|
41
|
+
subtitle: "Read from config/plugins.js and the server environment. Nothing here is edited in the admin; secrets are masked."
|
|
42
|
+
}
|
|
43
|
+
),
|
|
44
|
+
/* @__PURE__ */ jsxRuntime.jsxs(admin.Layouts.Content, { children: [
|
|
45
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
46
|
+
api.Intro,
|
|
47
|
+
{
|
|
48
|
+
id: "settings",
|
|
49
|
+
title: "How a host app talks to the game",
|
|
50
|
+
body: [
|
|
51
|
+
"The plugin does not do authentication of its own. Its player routes under /api/tcg are ordinary Strapi content-API routes: give an API token (Settings → API Tokens) or a Users & Permissions role the TCG actions you want to allow, and call the routes with that credential. Any Strapi can install the plugin and any host can drive it.",
|
|
52
|
+
"The caller names the player with a header (x-player-id by default) carrying any stable id it owns: a phone number, a Max It user id, a username. Unknown ids create a player on first sight (autoProvision), and an optional name and avatar header refresh the profile. Catalog routes (season, packs, cards, live, rewards) need no player header.",
|
|
53
|
+
"Out of the box no configuration is needed. Before an event set TCG_QR_SECRET so printed and rotating QR codes survive a rotation of Strapi’s app keys."
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
),
|
|
57
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { gap: 4, children: [
|
|
58
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 6, s: 12, alignItems: "stretch", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
59
|
+
api.Section,
|
|
60
|
+
{
|
|
61
|
+
eyebrow: "The contract",
|
|
62
|
+
title: "Player identity",
|
|
63
|
+
help: "Header names the caller must send on player routes.",
|
|
64
|
+
style: { width: "100%" },
|
|
65
|
+
children: [
|
|
66
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
67
|
+
Row,
|
|
68
|
+
{
|
|
69
|
+
k: "Player id header",
|
|
70
|
+
hint: "players.idHeader · required on every player route",
|
|
71
|
+
v: players.idHeader || "x-player-id",
|
|
72
|
+
tone: players.idHeader && players.idHeader !== "x-player-id" ? "success" : "warning"
|
|
73
|
+
}
|
|
74
|
+
),
|
|
75
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
76
|
+
Row,
|
|
77
|
+
{
|
|
78
|
+
k: "Display name header",
|
|
79
|
+
hint: "players.nameHeader · optional hint, refreshes the profile",
|
|
80
|
+
v: players.nameHeader || "x-player-name",
|
|
81
|
+
tone: players.nameHeader && players.nameHeader !== "x-player-name" ? "success" : "warning"
|
|
82
|
+
}
|
|
83
|
+
),
|
|
84
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
85
|
+
Row,
|
|
86
|
+
{
|
|
87
|
+
k: "Avatar header",
|
|
88
|
+
hint: "players.avatarHeader · optional URL",
|
|
89
|
+
v: players.avatarHeader || "x-player-avatar",
|
|
90
|
+
tone: players.avatarHeader && players.avatarHeader !== "x-player-avatar" ? "success" : "warning"
|
|
91
|
+
}
|
|
92
|
+
),
|
|
93
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
94
|
+
Row,
|
|
95
|
+
{
|
|
96
|
+
k: "Auto-provision",
|
|
97
|
+
hint: "players.autoProvision · create a player for an unknown id; off answers 404 PLAYER_NOT_FOUND",
|
|
98
|
+
v: players.autoProvision === false ? "off" : "on",
|
|
99
|
+
tone: players.autoProvision === false ? "success" : "warning"
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
) }),
|
|
105
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 6, s: 12, alignItems: "stretch", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
106
|
+
api.Section,
|
|
107
|
+
{
|
|
108
|
+
eyebrow: "Before an event",
|
|
109
|
+
title: "Secrets and defaults",
|
|
110
|
+
help: "Shown with their effective value.",
|
|
111
|
+
style: { width: "100%" },
|
|
112
|
+
children: [
|
|
113
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
114
|
+
Row,
|
|
115
|
+
{
|
|
116
|
+
k: "QR global secret",
|
|
117
|
+
hint: "TCG_QR_SECRET · signs trade QR codes and packs without their own secret; derived from the app keys when unset",
|
|
118
|
+
v: s.qr?.globalSecret || "derived from app keys",
|
|
119
|
+
tone: s.qr?.globalSecret ? "success" : "warning"
|
|
120
|
+
}
|
|
121
|
+
),
|
|
122
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
123
|
+
Row,
|
|
124
|
+
{
|
|
125
|
+
k: "Player code prefix",
|
|
126
|
+
hint: "TCG_PLAYER_CODE_PREFIX · the JMB in JMB-A7K2",
|
|
127
|
+
v: s.playerCodePrefix || "TCG",
|
|
128
|
+
tone: s.playerCodePrefix && s.playerCodePrefix !== "TCG" ? "success" : "warning"
|
|
129
|
+
}
|
|
130
|
+
),
|
|
131
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
132
|
+
Row,
|
|
133
|
+
{
|
|
134
|
+
k: "Background sweeps",
|
|
135
|
+
hint: "Expired QR nonces and trade offers are cleaned up by the server",
|
|
136
|
+
v: s.sweeps?.enabled === false ? "off" : "on",
|
|
137
|
+
tone: "warning"
|
|
138
|
+
}
|
|
139
|
+
)
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
) })
|
|
143
|
+
] }),
|
|
144
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 6, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { gap: 4, children: [
|
|
145
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 6, s: 12, alignItems: "stretch", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
146
|
+
api.Section,
|
|
147
|
+
{
|
|
148
|
+
eyebrow: "Copy / paste",
|
|
149
|
+
title: "config/plugins.js",
|
|
150
|
+
help: "Everything is optional; values come from the environment.",
|
|
151
|
+
style: { width: "100%" },
|
|
152
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(api.Mono, { variant: "pi", tag: "pre", style: { whiteSpace: "pre-wrap" }, children: `tcg: {
|
|
153
|
+
enabled: true,
|
|
154
|
+
config: {
|
|
155
|
+
playerCodePrefix: env('TCG_PLAYER_CODE_PREFIX', '${s.playerCodePrefix ?? "TCG"}'),
|
|
156
|
+
qr: { globalSecret: env('TCG_QR_SECRET', '') },
|
|
157
|
+
players: {
|
|
158
|
+
idHeader: '${players.idHeader ?? "x-player-id"}',
|
|
159
|
+
nameHeader: '${players.nameHeader ?? "x-player-name"}',
|
|
160
|
+
avatarHeader: '${players.avatarHeader ?? "x-player-avatar"}',
|
|
161
|
+
autoProvision: ${players.autoProvision === false ? "false" : "true"},
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
},` })
|
|
165
|
+
}
|
|
166
|
+
) }),
|
|
167
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 6, s: 12, alignItems: "stretch", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
168
|
+
api.Section,
|
|
169
|
+
{
|
|
170
|
+
eyebrow: "Copy / paste",
|
|
171
|
+
title: "A call from a host",
|
|
172
|
+
help: "An API token with the TCG actions, plus the player header.",
|
|
173
|
+
style: { width: "100%" },
|
|
174
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(api.Mono, { variant: "pi", tag: "pre", style: { whiteSpace: "pre-wrap" }, children: `POST /api/tcg/pulls/scan
|
|
175
|
+
Authorization: Bearer <strapi api token>
|
|
176
|
+
${players.idHeader ?? "x-player-id"}: 221770000001
|
|
177
|
+
${players.nameHeader ?? "x-player-name"}: Awa Diop
|
|
178
|
+
Content-Type: application/json
|
|
179
|
+
|
|
180
|
+
{ "qrToken": "p.…" }
|
|
181
|
+
|
|
182
|
+
→ 201 { "data": { "card": { … }, "mintNumber": 17, "isNew": true } }
|
|
183
|
+
→ 400 { "error": { "code": "PLAYER_REQUIRED" } } // header missing
|
|
184
|
+
→ 403 { "error": { "code": "PLAYER_INACTIVE" } } // deactivated in Players` })
|
|
185
|
+
}
|
|
186
|
+
) })
|
|
187
|
+
] }) })
|
|
188
|
+
] })
|
|
189
|
+
] }) });
|
|
190
|
+
};
|
|
191
|
+
exports.SettingsPage = SettingsPage;
|