@playcademy/sandbox 0.3.17-beta.35 → 0.3.17-beta.37
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/dist/cli.js +146 -92
- package/dist/server.js +146 -92
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1330,7 +1330,7 @@ var package_default;
|
|
|
1330
1330
|
var init_package = __esm(() => {
|
|
1331
1331
|
package_default = {
|
|
1332
1332
|
name: "@playcademy/sandbox",
|
|
1333
|
-
version: "0.3.17-beta.
|
|
1333
|
+
version: "0.3.17-beta.37",
|
|
1334
1334
|
description: "Local development server for Playcademy game development",
|
|
1335
1335
|
type: "module",
|
|
1336
1336
|
exports: {
|
|
@@ -11297,8 +11297,9 @@ var init_pg_core = __esm(() => {
|
|
|
11297
11297
|
});
|
|
11298
11298
|
|
|
11299
11299
|
// ../data/src/domains/game/table.ts
|
|
11300
|
-
var gamePlatformEnum, gameTypeEnum, gameVisibilityEnum, games, gameSessions, gameStates, deploymentProviderEnum, deployJobStatusEnum, gameDeployments, gameDeployJobs, customHostnameStatusEnum, customHostnameSslStatusEnum, customHostnameEnvironmentEnum, gameCustomHostnames;
|
|
11300
|
+
var gamePlatformEnum, gameTypeEnum, gameVisibilityEnum, games, gameMemberRoleEnum, gameMembers, gameMembersRelations, gameSessions, gameStates, deploymentProviderEnum, deployJobStatusEnum, gameDeployments, gameDeployJobs, customHostnameStatusEnum, customHostnameSslStatusEnum, customHostnameEnvironmentEnum, gameCustomHostnames;
|
|
11301
11301
|
var init_table3 = __esm(() => {
|
|
11302
|
+
init_drizzle_orm();
|
|
11302
11303
|
init_pg_core();
|
|
11303
11304
|
init_table5();
|
|
11304
11305
|
init_table6();
|
|
@@ -11307,9 +11308,6 @@ var init_table3 = __esm(() => {
|
|
|
11307
11308
|
gameVisibilityEnum = pgEnum("game_visibility", ["visible", "unlisted", "internal"]);
|
|
11308
11309
|
games = pgTable("games", {
|
|
11309
11310
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
11310
|
-
developerId: text("developer_id").references(() => users.id, {
|
|
11311
|
-
onDelete: "set null"
|
|
11312
|
-
}),
|
|
11313
11311
|
slug: varchar("slug", { length: 255 }).notNull().unique(),
|
|
11314
11312
|
displayName: varchar("display_name", { length: 255 }).notNull(),
|
|
11315
11313
|
version: varchar("version", { length: 50 }).notNull(),
|
|
@@ -11325,6 +11323,24 @@ var init_table3 = __esm(() => {
|
|
|
11325
11323
|
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
|
11326
11324
|
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow()
|
|
11327
11325
|
});
|
|
11326
|
+
gameMemberRoleEnum = pgEnum("game_member_role", ["owner", "collaborator"]);
|
|
11327
|
+
gameMembers = pgTable("game_members", {
|
|
11328
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
11329
|
+
gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
|
|
11330
|
+
userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
11331
|
+
role: gameMemberRoleEnum("role").notNull().default("collaborator"),
|
|
11332
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
11333
|
+
}, (table3) => [uniqueIndex("game_members_game_user_idx").on(table3.gameId, table3.userId)]);
|
|
11334
|
+
gameMembersRelations = relations(gameMembers, ({ one }) => ({
|
|
11335
|
+
game: one(games, {
|
|
11336
|
+
fields: [gameMembers.gameId],
|
|
11337
|
+
references: [games.id]
|
|
11338
|
+
}),
|
|
11339
|
+
user: one(users, {
|
|
11340
|
+
fields: [gameMembers.userId],
|
|
11341
|
+
references: [users.id]
|
|
11342
|
+
})
|
|
11343
|
+
}));
|
|
11328
11344
|
gameSessions = pgTable("game_sessions", {
|
|
11329
11345
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
11330
11346
|
userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
@@ -12142,6 +12158,9 @@ __export(exports_tables_index, {
|
|
|
12142
12158
|
gameScoresRelations: () => gameScoresRelations,
|
|
12143
12159
|
gameScores: () => gameScores,
|
|
12144
12160
|
gamePlatformEnum: () => gamePlatformEnum,
|
|
12161
|
+
gameMembersRelations: () => gameMembersRelations,
|
|
12162
|
+
gameMembers: () => gameMembers,
|
|
12163
|
+
gameMemberRoleEnum: () => gameMemberRoleEnum,
|
|
12145
12164
|
gameDeployments: () => gameDeployments,
|
|
12146
12165
|
gameDeployJobs: () => gameDeployJobs,
|
|
12147
12166
|
gameCustomHostnames: () => gameCustomHostnames,
|
|
@@ -26776,29 +26795,33 @@ var init_game_service = __esm(() => {
|
|
|
26776
26795
|
const db2 = this.deps.db;
|
|
26777
26796
|
const isAdmin = caller?.role === "admin";
|
|
26778
26797
|
const isDeveloper = caller?.role === "developer";
|
|
26779
|
-
let whereClause;
|
|
26780
26798
|
if (isAdmin) {
|
|
26781
|
-
|
|
26782
|
-
|
|
26783
|
-
|
|
26784
|
-
}
|
|
26785
|
-
|
|
26799
|
+
return db2.query.games.findMany({
|
|
26800
|
+
orderBy: [desc(games.createdAt)]
|
|
26801
|
+
});
|
|
26802
|
+
}
|
|
26803
|
+
if (isDeveloper && caller?.id) {
|
|
26804
|
+
const rows = await db2.select().from(games).where(or(ne(games.visibility, "internal"), exists(db2.select({ one: sql`1` }).from(gameMembers).where(and(eq(gameMembers.gameId, games.id), eq(gameMembers.userId, caller.id)))))).orderBy(desc(games.createdAt));
|
|
26805
|
+
return rows;
|
|
26786
26806
|
}
|
|
26787
26807
|
return db2.query.games.findMany({
|
|
26788
|
-
where:
|
|
26808
|
+
where: ne(games.visibility, "internal"),
|
|
26789
26809
|
orderBy: [desc(games.createdAt)]
|
|
26790
26810
|
});
|
|
26791
26811
|
}
|
|
26792
|
-
async
|
|
26812
|
+
async listAccessible(user) {
|
|
26793
26813
|
const seesAllGames = user.role === "admin" || user.role === "teacher";
|
|
26794
26814
|
if (!seesAllGames) {
|
|
26795
26815
|
this.validateDeveloperStatus(user);
|
|
26796
26816
|
}
|
|
26797
26817
|
const db2 = this.deps.db;
|
|
26798
|
-
|
|
26799
|
-
|
|
26800
|
-
|
|
26801
|
-
|
|
26818
|
+
if (seesAllGames) {
|
|
26819
|
+
return db2.query.games.findMany({
|
|
26820
|
+
orderBy: [desc(games.createdAt)]
|
|
26821
|
+
});
|
|
26822
|
+
}
|
|
26823
|
+
const rows = await db2.select({ games }).from(games).innerJoin(gameMembers, eq(gameMembers.gameId, games.id)).where(eq(gameMembers.userId, user.id)).orderBy(desc(games.createdAt));
|
|
26824
|
+
return rows.map((r) => r.games);
|
|
26802
26825
|
}
|
|
26803
26826
|
async getSubjects() {
|
|
26804
26827
|
const db2 = this.deps.db;
|
|
@@ -26822,7 +26845,7 @@ var init_game_service = __esm(() => {
|
|
|
26822
26845
|
if (!game) {
|
|
26823
26846
|
throw new NotFoundError("Game", gameId);
|
|
26824
26847
|
}
|
|
26825
|
-
this.enforceVisibility(game, caller, gameId);
|
|
26848
|
+
await this.enforceVisibility(game, caller, gameId);
|
|
26826
26849
|
return game;
|
|
26827
26850
|
}
|
|
26828
26851
|
async getBySlug(slug, caller) {
|
|
@@ -26833,7 +26856,7 @@ var init_game_service = __esm(() => {
|
|
|
26833
26856
|
if (!game) {
|
|
26834
26857
|
throw new NotFoundError("Game", slug);
|
|
26835
26858
|
}
|
|
26836
|
-
this.enforceVisibility(game, caller, slug);
|
|
26859
|
+
await this.enforceVisibility(game, caller, slug);
|
|
26837
26860
|
return game;
|
|
26838
26861
|
}
|
|
26839
26862
|
async getManifest(gameId, caller) {
|
|
@@ -26999,15 +27022,20 @@ var init_game_service = __esm(() => {
|
|
|
26999
27022
|
};
|
|
27000
27023
|
}
|
|
27001
27024
|
}
|
|
27002
|
-
enforceVisibility(game, caller, lookupIdentifier) {
|
|
27025
|
+
async enforceVisibility(game, caller, lookupIdentifier) {
|
|
27003
27026
|
if (game.visibility !== "internal") {
|
|
27004
27027
|
return;
|
|
27005
27028
|
}
|
|
27006
|
-
|
|
27007
|
-
const isOwner = caller?.id != null && caller.id === game.developerId;
|
|
27008
|
-
if (!isAdmin && !isOwner) {
|
|
27029
|
+
if (!caller) {
|
|
27009
27030
|
throw new NotFoundError("Game", lookupIdentifier);
|
|
27010
27031
|
}
|
|
27032
|
+
if (caller.role === "admin") {
|
|
27033
|
+
return;
|
|
27034
|
+
}
|
|
27035
|
+
if (await this.hasGameMembership(game.id, caller.id)) {
|
|
27036
|
+
return;
|
|
27037
|
+
}
|
|
27038
|
+
throw new NotFoundError("Game", lookupIdentifier);
|
|
27011
27039
|
}
|
|
27012
27040
|
async upsertBySlug(slug, data, user) {
|
|
27013
27041
|
const db2 = this.deps.db;
|
|
@@ -27044,17 +27072,24 @@ var init_game_service = __esm(() => {
|
|
|
27044
27072
|
...gameDataForDb,
|
|
27045
27073
|
id: gameId,
|
|
27046
27074
|
slug,
|
|
27047
|
-
developerId: user.id,
|
|
27048
27075
|
metadata: data.metadata || {},
|
|
27049
27076
|
version: data.gameType === "external" ? "external" : "",
|
|
27050
27077
|
deploymentUrl: null,
|
|
27051
27078
|
createdAt: new Date
|
|
27052
27079
|
};
|
|
27053
|
-
const
|
|
27054
|
-
|
|
27055
|
-
|
|
27056
|
-
|
|
27057
|
-
|
|
27080
|
+
const createdGame = await db2.transaction(async (tx) => {
|
|
27081
|
+
const [game] = await tx.insert(games).values(insertData).returning();
|
|
27082
|
+
if (!game) {
|
|
27083
|
+
logger5.error("Game insert returned no rows", { slug, userId: user.id });
|
|
27084
|
+
throw new InternalError("DB insert failed to return result for new game");
|
|
27085
|
+
}
|
|
27086
|
+
await tx.insert(gameMembers).values({
|
|
27087
|
+
gameId: game.id,
|
|
27088
|
+
userId: user.id,
|
|
27089
|
+
role: "owner"
|
|
27090
|
+
});
|
|
27091
|
+
return game;
|
|
27092
|
+
});
|
|
27058
27093
|
gameResponse = createdGame;
|
|
27059
27094
|
}
|
|
27060
27095
|
if (data.mapElementId) {
|
|
@@ -27152,51 +27187,43 @@ var init_game_service = __esm(() => {
|
|
|
27152
27187
|
displayName: gameToDelete.displayName
|
|
27153
27188
|
};
|
|
27154
27189
|
}
|
|
27190
|
+
async hasGameMembership(gameId, userId) {
|
|
27191
|
+
const membership = await this.deps.db.query.gameMembers.findFirst({
|
|
27192
|
+
where: and(eq(gameMembers.gameId, gameId), eq(gameMembers.userId, userId)),
|
|
27193
|
+
columns: { id: true }
|
|
27194
|
+
});
|
|
27195
|
+
return Boolean(membership);
|
|
27196
|
+
}
|
|
27155
27197
|
async validateOwnership(user, gameId) {
|
|
27156
|
-
if (user.role === "admin") {
|
|
27157
|
-
const gameExists = await this.deps.db.query.games.findFirst({
|
|
27158
|
-
where: eq(games.id, gameId),
|
|
27159
|
-
columns: { id: true }
|
|
27160
|
-
});
|
|
27161
|
-
if (!gameExists) {
|
|
27162
|
-
throw new NotFoundError("Game", gameId);
|
|
27163
|
-
}
|
|
27164
|
-
return;
|
|
27165
|
-
}
|
|
27166
27198
|
const db2 = this.deps.db;
|
|
27167
|
-
const
|
|
27168
|
-
where:
|
|
27199
|
+
const gameExists = await db2.query.games.findFirst({
|
|
27200
|
+
where: eq(games.id, gameId),
|
|
27169
27201
|
columns: { id: true }
|
|
27170
27202
|
});
|
|
27171
|
-
if (!
|
|
27172
|
-
|
|
27173
|
-
|
|
27174
|
-
|
|
27175
|
-
|
|
27176
|
-
|
|
27177
|
-
|
|
27178
|
-
}
|
|
27203
|
+
if (!gameExists) {
|
|
27204
|
+
throw new NotFoundError("Game", gameId);
|
|
27205
|
+
}
|
|
27206
|
+
if (user.role === "admin") {
|
|
27207
|
+
return;
|
|
27208
|
+
}
|
|
27209
|
+
if (!await this.hasGameMembership(gameId, user.id)) {
|
|
27179
27210
|
throw new AccessDeniedError("You do not own this game");
|
|
27180
27211
|
}
|
|
27181
27212
|
}
|
|
27182
27213
|
async validateDeveloperAccess(user, gameId) {
|
|
27183
27214
|
this.validateDeveloperStatus(user);
|
|
27184
|
-
if (user.role === "admin") {
|
|
27185
|
-
const gameExists = await this.deps.db.query.games.findFirst({
|
|
27186
|
-
where: eq(games.id, gameId),
|
|
27187
|
-
columns: { id: true }
|
|
27188
|
-
});
|
|
27189
|
-
if (!gameExists) {
|
|
27190
|
-
throw new NotFoundError("Game", gameId);
|
|
27191
|
-
}
|
|
27192
|
-
return;
|
|
27193
|
-
}
|
|
27194
27215
|
const db2 = this.deps.db;
|
|
27195
|
-
const
|
|
27196
|
-
where:
|
|
27216
|
+
const gameExists = await db2.query.games.findFirst({
|
|
27217
|
+
where: eq(games.id, gameId),
|
|
27197
27218
|
columns: { id: true }
|
|
27198
27219
|
});
|
|
27199
|
-
if (!
|
|
27220
|
+
if (!gameExists) {
|
|
27221
|
+
throw new NotFoundError("Game", gameId);
|
|
27222
|
+
}
|
|
27223
|
+
if (user.role === "admin") {
|
|
27224
|
+
return;
|
|
27225
|
+
}
|
|
27226
|
+
if (!await this.hasGameMembership(gameId, user.id)) {
|
|
27200
27227
|
throw new NotFoundError("Game", gameId);
|
|
27201
27228
|
}
|
|
27202
27229
|
}
|
|
@@ -27216,21 +27243,18 @@ var init_game_service = __esm(() => {
|
|
|
27216
27243
|
async validateDeveloperAccessBySlug(user, slug) {
|
|
27217
27244
|
this.validateDeveloperStatus(user);
|
|
27218
27245
|
const db2 = this.deps.db;
|
|
27219
|
-
if (user.role === "admin") {
|
|
27220
|
-
const game2 = await db2.query.games.findFirst({
|
|
27221
|
-
where: eq(games.slug, slug)
|
|
27222
|
-
});
|
|
27223
|
-
if (!game2) {
|
|
27224
|
-
throw new NotFoundError("Game", slug);
|
|
27225
|
-
}
|
|
27226
|
-
return game2;
|
|
27227
|
-
}
|
|
27228
27246
|
const game = await db2.query.games.findFirst({
|
|
27229
|
-
where:
|
|
27247
|
+
where: eq(games.slug, slug)
|
|
27230
27248
|
});
|
|
27231
27249
|
if (!game) {
|
|
27232
27250
|
throw new NotFoundError("Game", slug);
|
|
27233
27251
|
}
|
|
27252
|
+
if (user.role === "admin") {
|
|
27253
|
+
return game;
|
|
27254
|
+
}
|
|
27255
|
+
if (!await this.hasGameMembership(game.id, user.id)) {
|
|
27256
|
+
throw new NotFoundError("Game", slug);
|
|
27257
|
+
}
|
|
27234
27258
|
return game;
|
|
27235
27259
|
}
|
|
27236
27260
|
validateDeveloperStatus(user) {
|
|
@@ -34879,10 +34903,17 @@ class LogsService {
|
|
|
34879
34903
|
throw new AccessDeniedError("Must be an approved developer");
|
|
34880
34904
|
}
|
|
34881
34905
|
const game = await db2.query.games.findFirst({
|
|
34882
|
-
where:
|
|
34906
|
+
where: eq(games.slug, slug2),
|
|
34883
34907
|
columns: { id: true }
|
|
34884
34908
|
});
|
|
34885
34909
|
if (!game) {
|
|
34910
|
+
throw new NotFoundError("Game", slug2);
|
|
34911
|
+
}
|
|
34912
|
+
const membership = await db2.query.gameMembers.findFirst({
|
|
34913
|
+
where: and(eq(gameMembers.gameId, game.id), eq(gameMembers.userId, user.id)),
|
|
34914
|
+
columns: { id: true }
|
|
34915
|
+
});
|
|
34916
|
+
if (!membership) {
|
|
34886
34917
|
logger28.warn("Developer attempted access to unowned game logs", {
|
|
34887
34918
|
userId: user.id,
|
|
34888
34919
|
slug: slug2
|
|
@@ -93763,7 +93794,6 @@ async function seedCoreGames(db2) {
|
|
|
93763
93794
|
const coreGames = [
|
|
93764
93795
|
{
|
|
93765
93796
|
id: CORE_GAME_UUIDS.PLAYGROUND,
|
|
93766
|
-
developerId: DEMO_USERS.developer.id,
|
|
93767
93797
|
slug: "playground",
|
|
93768
93798
|
displayName: "Playground",
|
|
93769
93799
|
version: "local",
|
|
@@ -93780,6 +93810,11 @@ async function seedCoreGames(db2) {
|
|
|
93780
93810
|
for (const gameData of coreGames) {
|
|
93781
93811
|
try {
|
|
93782
93812
|
await db2.insert(games).values(gameData).onConflictDoNothing();
|
|
93813
|
+
await db2.insert(gameMembers).values({
|
|
93814
|
+
gameId: gameData.id,
|
|
93815
|
+
userId: DEMO_USERS.developer.id,
|
|
93816
|
+
role: "owner"
|
|
93817
|
+
}).onConflictDoNothing();
|
|
93783
93818
|
} catch (error2) {
|
|
93784
93819
|
logger37.error(`Error seeding core game '${gameData.slug}': ${error2}`);
|
|
93785
93820
|
}
|
|
@@ -93804,7 +93839,6 @@ async function seedCurrentProjectGame(db2, project) {
|
|
|
93804
93839
|
}
|
|
93805
93840
|
const gameRecord = {
|
|
93806
93841
|
id: desiredGameId ?? crypto.randomUUID(),
|
|
93807
|
-
developerId: DEMO_USERS.developer.id,
|
|
93808
93842
|
slug: project.slug,
|
|
93809
93843
|
displayName: project.displayName,
|
|
93810
93844
|
version: project.version,
|
|
@@ -93822,6 +93856,11 @@ async function seedCurrentProjectGame(db2, project) {
|
|
|
93822
93856
|
if (!newGame) {
|
|
93823
93857
|
throw new Error("Failed to create game record");
|
|
93824
93858
|
}
|
|
93859
|
+
await db2.insert(gameMembers).values({
|
|
93860
|
+
gameId: newGame.id,
|
|
93861
|
+
userId: DEMO_USERS.developer.id,
|
|
93862
|
+
role: "owner"
|
|
93863
|
+
}).onConflictDoNothing();
|
|
93825
93864
|
if (project.timebackCourses && project.timebackCourses.length > 0) {
|
|
93826
93865
|
await seedTimebackIntegrations(db2, newGame.id, project.timebackCourses);
|
|
93827
93866
|
}
|
|
@@ -94810,7 +94849,6 @@ var init_schemas2 = __esm(() => {
|
|
|
94810
94849
|
id: true,
|
|
94811
94850
|
slug: true,
|
|
94812
94851
|
createdAt: true,
|
|
94813
|
-
developerId: true,
|
|
94814
94852
|
version: true
|
|
94815
94853
|
}).refine((data) => {
|
|
94816
94854
|
if (data.gameType === "hosted" && data.deploymentUrl === null) {
|
|
@@ -96212,7 +96250,7 @@ var init_domain_controller = __esm(() => {
|
|
|
96212
96250
|
});
|
|
96213
96251
|
|
|
96214
96252
|
// ../api-core/src/controllers/game.controller.ts
|
|
96215
|
-
var logger48, list3,
|
|
96253
|
+
var logger48, list3, listAccessible, getSubjects, getById2, getBySlug, getManifest, upsertBySlug, remove3, games2;
|
|
96216
96254
|
var init_game_controller = __esm(() => {
|
|
96217
96255
|
init_esm();
|
|
96218
96256
|
init_schemas_index();
|
|
@@ -96225,9 +96263,9 @@ var init_game_controller = __esm(() => {
|
|
|
96225
96263
|
logger48.debug("Listing games", { userId: ctx.user.id });
|
|
96226
96264
|
return ctx.services.game.list(ctx.user);
|
|
96227
96265
|
});
|
|
96228
|
-
|
|
96229
|
-
logger48.debug("Listing
|
|
96230
|
-
return ctx.services.game.
|
|
96266
|
+
listAccessible = requireNonAnonymous(async (ctx) => {
|
|
96267
|
+
logger48.debug("Listing accessible games", { userId: ctx.user.id });
|
|
96268
|
+
return ctx.services.game.listAccessible(ctx.user);
|
|
96231
96269
|
});
|
|
96232
96270
|
getSubjects = requireNonAnonymous(async (ctx) => {
|
|
96233
96271
|
logger48.debug("Getting game subjects", { userId: ctx.user.id });
|
|
@@ -96300,7 +96338,7 @@ var init_game_controller = __esm(() => {
|
|
|
96300
96338
|
});
|
|
96301
96339
|
games2 = {
|
|
96302
96340
|
list: list3,
|
|
96303
|
-
|
|
96341
|
+
listAccessible,
|
|
96304
96342
|
getSubjects,
|
|
96305
96343
|
getById: getById2,
|
|
96306
96344
|
getManifest,
|
|
@@ -98421,6 +98459,7 @@ var init_crud = __esm(() => {
|
|
|
98421
98459
|
init_api();
|
|
98422
98460
|
gameCrudRouter = new Hono2;
|
|
98423
98461
|
gameCrudRouter.get("/", handle2(games2.list));
|
|
98462
|
+
gameCrudRouter.get("/accessible", handle2(games2.listAccessible));
|
|
98424
98463
|
gameCrudRouter.get("/:gameId{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}}/manifest", handle2(games2.getManifest));
|
|
98425
98464
|
gameCrudRouter.get("/:gameId{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}}", handle2(games2.getById));
|
|
98426
98465
|
gameCrudRouter.get("/:slug", handle2(games2.getBySlug));
|
|
@@ -98563,10 +98602,12 @@ var init_deploy = __esm(() => {
|
|
|
98563
98602
|
const db2 = ctx.db;
|
|
98564
98603
|
const existingGames = await db2.select().from(games).where(eq(games.slug, slug2));
|
|
98565
98604
|
const existingGame = existingGames[0];
|
|
98566
|
-
if (existingGame) {
|
|
98567
|
-
const
|
|
98568
|
-
|
|
98569
|
-
|
|
98605
|
+
if (existingGame && user.role !== "admin") {
|
|
98606
|
+
const membership = await db2.query.gameMembers.findFirst({
|
|
98607
|
+
where: and(eq(gameMembers.gameId, existingGame.id), eq(gameMembers.userId, user.id)),
|
|
98608
|
+
columns: { id: true }
|
|
98609
|
+
});
|
|
98610
|
+
if (!membership) {
|
|
98570
98611
|
return c2.json({
|
|
98571
98612
|
error: {
|
|
98572
98613
|
code: "FORBIDDEN",
|
|
@@ -98590,17 +98631,24 @@ var init_deploy = __esm(() => {
|
|
|
98590
98631
|
if (!body2.metadata?.displayName) {
|
|
98591
98632
|
return c2.json({ error: { code: "BAD_REQUEST", message: "Display name required for new game" } }, 400);
|
|
98592
98633
|
}
|
|
98593
|
-
const
|
|
98634
|
+
const gameValues = {
|
|
98594
98635
|
slug: slug2,
|
|
98595
98636
|
displayName: body2.metadata.displayName,
|
|
98596
98637
|
version: "1.0.0",
|
|
98597
98638
|
platform: body2.metadata.platform ?? "web",
|
|
98598
98639
|
gameType: "hosted",
|
|
98599
|
-
developerId: user.id,
|
|
98600
98640
|
deploymentUrl: `http://localhost:4321`,
|
|
98601
98641
|
metadata: body2.metadata.metadata ?? {}
|
|
98602
|
-
}
|
|
98603
|
-
game =
|
|
98642
|
+
};
|
|
98643
|
+
game = await db2.transaction(async (tx) => {
|
|
98644
|
+
const [inserted] = await tx.insert(games).values(gameValues).returning();
|
|
98645
|
+
await tx.insert(gameMembers).values({
|
|
98646
|
+
gameId: inserted.id,
|
|
98647
|
+
userId: user.id,
|
|
98648
|
+
role: "owner"
|
|
98649
|
+
});
|
|
98650
|
+
return inserted;
|
|
98651
|
+
});
|
|
98604
98652
|
}
|
|
98605
98653
|
let backendCode = body2.code;
|
|
98606
98654
|
if (!backendCode && body2.codeUploadToken) {
|
|
@@ -98679,13 +98727,19 @@ var init_deploy = __esm(() => {
|
|
|
98679
98727
|
const ctx = getSandboxContext();
|
|
98680
98728
|
const game = await ctx.db.query.games.findFirst({
|
|
98681
98729
|
where: eq(games.slug, slug2 ?? ""),
|
|
98682
|
-
columns: { id: true
|
|
98730
|
+
columns: { id: true }
|
|
98683
98731
|
});
|
|
98684
98732
|
if (!game) {
|
|
98685
98733
|
return c2.json({ error: { code: "NOT_FOUND", message: "Game not found" } }, 404);
|
|
98686
98734
|
}
|
|
98687
|
-
if (
|
|
98688
|
-
|
|
98735
|
+
if (user.role !== "admin") {
|
|
98736
|
+
const membership = await ctx.db.query.gameMembers.findFirst({
|
|
98737
|
+
where: and(eq(gameMembers.gameId, game.id), eq(gameMembers.userId, user.id)),
|
|
98738
|
+
columns: { id: true }
|
|
98739
|
+
});
|
|
98740
|
+
if (!membership) {
|
|
98741
|
+
return c2.json({ error: { code: "NOT_FOUND", message: "Game not found" } }, 404);
|
|
98742
|
+
}
|
|
98689
98743
|
}
|
|
98690
98744
|
const job = await ctx.db.query.gameDeployJobs.findFirst({
|
|
98691
98745
|
where: and(eq(gameDeployJobs.id, jobId), eq(gameDeployJobs.gameId, game.id))
|
package/dist/server.js
CHANGED
|
@@ -1329,7 +1329,7 @@ var package_default;
|
|
|
1329
1329
|
var init_package = __esm(() => {
|
|
1330
1330
|
package_default = {
|
|
1331
1331
|
name: "@playcademy/sandbox",
|
|
1332
|
-
version: "0.3.17-beta.
|
|
1332
|
+
version: "0.3.17-beta.37",
|
|
1333
1333
|
description: "Local development server for Playcademy game development",
|
|
1334
1334
|
type: "module",
|
|
1335
1335
|
exports: {
|
|
@@ -11296,8 +11296,9 @@ var init_pg_core = __esm(() => {
|
|
|
11296
11296
|
});
|
|
11297
11297
|
|
|
11298
11298
|
// ../data/src/domains/game/table.ts
|
|
11299
|
-
var gamePlatformEnum, gameTypeEnum, gameVisibilityEnum, games, gameSessions, gameStates, deploymentProviderEnum, deployJobStatusEnum, gameDeployments, gameDeployJobs, customHostnameStatusEnum, customHostnameSslStatusEnum, customHostnameEnvironmentEnum, gameCustomHostnames;
|
|
11299
|
+
var gamePlatformEnum, gameTypeEnum, gameVisibilityEnum, games, gameMemberRoleEnum, gameMembers, gameMembersRelations, gameSessions, gameStates, deploymentProviderEnum, deployJobStatusEnum, gameDeployments, gameDeployJobs, customHostnameStatusEnum, customHostnameSslStatusEnum, customHostnameEnvironmentEnum, gameCustomHostnames;
|
|
11300
11300
|
var init_table3 = __esm(() => {
|
|
11301
|
+
init_drizzle_orm();
|
|
11301
11302
|
init_pg_core();
|
|
11302
11303
|
init_table5();
|
|
11303
11304
|
init_table6();
|
|
@@ -11306,9 +11307,6 @@ var init_table3 = __esm(() => {
|
|
|
11306
11307
|
gameVisibilityEnum = pgEnum("game_visibility", ["visible", "unlisted", "internal"]);
|
|
11307
11308
|
games = pgTable("games", {
|
|
11308
11309
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
11309
|
-
developerId: text("developer_id").references(() => users.id, {
|
|
11310
|
-
onDelete: "set null"
|
|
11311
|
-
}),
|
|
11312
11310
|
slug: varchar("slug", { length: 255 }).notNull().unique(),
|
|
11313
11311
|
displayName: varchar("display_name", { length: 255 }).notNull(),
|
|
11314
11312
|
version: varchar("version", { length: 50 }).notNull(),
|
|
@@ -11324,6 +11322,24 @@ var init_table3 = __esm(() => {
|
|
|
11324
11322
|
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
|
11325
11323
|
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow()
|
|
11326
11324
|
});
|
|
11325
|
+
gameMemberRoleEnum = pgEnum("game_member_role", ["owner", "collaborator"]);
|
|
11326
|
+
gameMembers = pgTable("game_members", {
|
|
11327
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
11328
|
+
gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
|
|
11329
|
+
userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
11330
|
+
role: gameMemberRoleEnum("role").notNull().default("collaborator"),
|
|
11331
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
11332
|
+
}, (table3) => [uniqueIndex("game_members_game_user_idx").on(table3.gameId, table3.userId)]);
|
|
11333
|
+
gameMembersRelations = relations(gameMembers, ({ one }) => ({
|
|
11334
|
+
game: one(games, {
|
|
11335
|
+
fields: [gameMembers.gameId],
|
|
11336
|
+
references: [games.id]
|
|
11337
|
+
}),
|
|
11338
|
+
user: one(users, {
|
|
11339
|
+
fields: [gameMembers.userId],
|
|
11340
|
+
references: [users.id]
|
|
11341
|
+
})
|
|
11342
|
+
}));
|
|
11327
11343
|
gameSessions = pgTable("game_sessions", {
|
|
11328
11344
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
11329
11345
|
userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
@@ -12141,6 +12157,9 @@ __export(exports_tables_index, {
|
|
|
12141
12157
|
gameScoresRelations: () => gameScoresRelations,
|
|
12142
12158
|
gameScores: () => gameScores,
|
|
12143
12159
|
gamePlatformEnum: () => gamePlatformEnum,
|
|
12160
|
+
gameMembersRelations: () => gameMembersRelations,
|
|
12161
|
+
gameMembers: () => gameMembers,
|
|
12162
|
+
gameMemberRoleEnum: () => gameMemberRoleEnum,
|
|
12144
12163
|
gameDeployments: () => gameDeployments,
|
|
12145
12164
|
gameDeployJobs: () => gameDeployJobs,
|
|
12146
12165
|
gameCustomHostnames: () => gameCustomHostnames,
|
|
@@ -26775,29 +26794,33 @@ var init_game_service = __esm(() => {
|
|
|
26775
26794
|
const db2 = this.deps.db;
|
|
26776
26795
|
const isAdmin = caller?.role === "admin";
|
|
26777
26796
|
const isDeveloper = caller?.role === "developer";
|
|
26778
|
-
let whereClause;
|
|
26779
26797
|
if (isAdmin) {
|
|
26780
|
-
|
|
26781
|
-
|
|
26782
|
-
|
|
26783
|
-
}
|
|
26784
|
-
|
|
26798
|
+
return db2.query.games.findMany({
|
|
26799
|
+
orderBy: [desc(games.createdAt)]
|
|
26800
|
+
});
|
|
26801
|
+
}
|
|
26802
|
+
if (isDeveloper && caller?.id) {
|
|
26803
|
+
const rows = await db2.select().from(games).where(or(ne(games.visibility, "internal"), exists(db2.select({ one: sql`1` }).from(gameMembers).where(and(eq(gameMembers.gameId, games.id), eq(gameMembers.userId, caller.id)))))).orderBy(desc(games.createdAt));
|
|
26804
|
+
return rows;
|
|
26785
26805
|
}
|
|
26786
26806
|
return db2.query.games.findMany({
|
|
26787
|
-
where:
|
|
26807
|
+
where: ne(games.visibility, "internal"),
|
|
26788
26808
|
orderBy: [desc(games.createdAt)]
|
|
26789
26809
|
});
|
|
26790
26810
|
}
|
|
26791
|
-
async
|
|
26811
|
+
async listAccessible(user) {
|
|
26792
26812
|
const seesAllGames = user.role === "admin" || user.role === "teacher";
|
|
26793
26813
|
if (!seesAllGames) {
|
|
26794
26814
|
this.validateDeveloperStatus(user);
|
|
26795
26815
|
}
|
|
26796
26816
|
const db2 = this.deps.db;
|
|
26797
|
-
|
|
26798
|
-
|
|
26799
|
-
|
|
26800
|
-
|
|
26817
|
+
if (seesAllGames) {
|
|
26818
|
+
return db2.query.games.findMany({
|
|
26819
|
+
orderBy: [desc(games.createdAt)]
|
|
26820
|
+
});
|
|
26821
|
+
}
|
|
26822
|
+
const rows = await db2.select({ games }).from(games).innerJoin(gameMembers, eq(gameMembers.gameId, games.id)).where(eq(gameMembers.userId, user.id)).orderBy(desc(games.createdAt));
|
|
26823
|
+
return rows.map((r) => r.games);
|
|
26801
26824
|
}
|
|
26802
26825
|
async getSubjects() {
|
|
26803
26826
|
const db2 = this.deps.db;
|
|
@@ -26821,7 +26844,7 @@ var init_game_service = __esm(() => {
|
|
|
26821
26844
|
if (!game) {
|
|
26822
26845
|
throw new NotFoundError("Game", gameId);
|
|
26823
26846
|
}
|
|
26824
|
-
this.enforceVisibility(game, caller, gameId);
|
|
26847
|
+
await this.enforceVisibility(game, caller, gameId);
|
|
26825
26848
|
return game;
|
|
26826
26849
|
}
|
|
26827
26850
|
async getBySlug(slug, caller) {
|
|
@@ -26832,7 +26855,7 @@ var init_game_service = __esm(() => {
|
|
|
26832
26855
|
if (!game) {
|
|
26833
26856
|
throw new NotFoundError("Game", slug);
|
|
26834
26857
|
}
|
|
26835
|
-
this.enforceVisibility(game, caller, slug);
|
|
26858
|
+
await this.enforceVisibility(game, caller, slug);
|
|
26836
26859
|
return game;
|
|
26837
26860
|
}
|
|
26838
26861
|
async getManifest(gameId, caller) {
|
|
@@ -26998,15 +27021,20 @@ var init_game_service = __esm(() => {
|
|
|
26998
27021
|
};
|
|
26999
27022
|
}
|
|
27000
27023
|
}
|
|
27001
|
-
enforceVisibility(game, caller, lookupIdentifier) {
|
|
27024
|
+
async enforceVisibility(game, caller, lookupIdentifier) {
|
|
27002
27025
|
if (game.visibility !== "internal") {
|
|
27003
27026
|
return;
|
|
27004
27027
|
}
|
|
27005
|
-
|
|
27006
|
-
const isOwner = caller?.id != null && caller.id === game.developerId;
|
|
27007
|
-
if (!isAdmin && !isOwner) {
|
|
27028
|
+
if (!caller) {
|
|
27008
27029
|
throw new NotFoundError("Game", lookupIdentifier);
|
|
27009
27030
|
}
|
|
27031
|
+
if (caller.role === "admin") {
|
|
27032
|
+
return;
|
|
27033
|
+
}
|
|
27034
|
+
if (await this.hasGameMembership(game.id, caller.id)) {
|
|
27035
|
+
return;
|
|
27036
|
+
}
|
|
27037
|
+
throw new NotFoundError("Game", lookupIdentifier);
|
|
27010
27038
|
}
|
|
27011
27039
|
async upsertBySlug(slug, data, user) {
|
|
27012
27040
|
const db2 = this.deps.db;
|
|
@@ -27043,17 +27071,24 @@ var init_game_service = __esm(() => {
|
|
|
27043
27071
|
...gameDataForDb,
|
|
27044
27072
|
id: gameId,
|
|
27045
27073
|
slug,
|
|
27046
|
-
developerId: user.id,
|
|
27047
27074
|
metadata: data.metadata || {},
|
|
27048
27075
|
version: data.gameType === "external" ? "external" : "",
|
|
27049
27076
|
deploymentUrl: null,
|
|
27050
27077
|
createdAt: new Date
|
|
27051
27078
|
};
|
|
27052
|
-
const
|
|
27053
|
-
|
|
27054
|
-
|
|
27055
|
-
|
|
27056
|
-
|
|
27079
|
+
const createdGame = await db2.transaction(async (tx) => {
|
|
27080
|
+
const [game] = await tx.insert(games).values(insertData).returning();
|
|
27081
|
+
if (!game) {
|
|
27082
|
+
logger5.error("Game insert returned no rows", { slug, userId: user.id });
|
|
27083
|
+
throw new InternalError("DB insert failed to return result for new game");
|
|
27084
|
+
}
|
|
27085
|
+
await tx.insert(gameMembers).values({
|
|
27086
|
+
gameId: game.id,
|
|
27087
|
+
userId: user.id,
|
|
27088
|
+
role: "owner"
|
|
27089
|
+
});
|
|
27090
|
+
return game;
|
|
27091
|
+
});
|
|
27057
27092
|
gameResponse = createdGame;
|
|
27058
27093
|
}
|
|
27059
27094
|
if (data.mapElementId) {
|
|
@@ -27151,51 +27186,43 @@ var init_game_service = __esm(() => {
|
|
|
27151
27186
|
displayName: gameToDelete.displayName
|
|
27152
27187
|
};
|
|
27153
27188
|
}
|
|
27189
|
+
async hasGameMembership(gameId, userId) {
|
|
27190
|
+
const membership = await this.deps.db.query.gameMembers.findFirst({
|
|
27191
|
+
where: and(eq(gameMembers.gameId, gameId), eq(gameMembers.userId, userId)),
|
|
27192
|
+
columns: { id: true }
|
|
27193
|
+
});
|
|
27194
|
+
return Boolean(membership);
|
|
27195
|
+
}
|
|
27154
27196
|
async validateOwnership(user, gameId) {
|
|
27155
|
-
if (user.role === "admin") {
|
|
27156
|
-
const gameExists = await this.deps.db.query.games.findFirst({
|
|
27157
|
-
where: eq(games.id, gameId),
|
|
27158
|
-
columns: { id: true }
|
|
27159
|
-
});
|
|
27160
|
-
if (!gameExists) {
|
|
27161
|
-
throw new NotFoundError("Game", gameId);
|
|
27162
|
-
}
|
|
27163
|
-
return;
|
|
27164
|
-
}
|
|
27165
27197
|
const db2 = this.deps.db;
|
|
27166
|
-
const
|
|
27167
|
-
where:
|
|
27198
|
+
const gameExists = await db2.query.games.findFirst({
|
|
27199
|
+
where: eq(games.id, gameId),
|
|
27168
27200
|
columns: { id: true }
|
|
27169
27201
|
});
|
|
27170
|
-
if (!
|
|
27171
|
-
|
|
27172
|
-
|
|
27173
|
-
|
|
27174
|
-
|
|
27175
|
-
|
|
27176
|
-
|
|
27177
|
-
}
|
|
27202
|
+
if (!gameExists) {
|
|
27203
|
+
throw new NotFoundError("Game", gameId);
|
|
27204
|
+
}
|
|
27205
|
+
if (user.role === "admin") {
|
|
27206
|
+
return;
|
|
27207
|
+
}
|
|
27208
|
+
if (!await this.hasGameMembership(gameId, user.id)) {
|
|
27178
27209
|
throw new AccessDeniedError("You do not own this game");
|
|
27179
27210
|
}
|
|
27180
27211
|
}
|
|
27181
27212
|
async validateDeveloperAccess(user, gameId) {
|
|
27182
27213
|
this.validateDeveloperStatus(user);
|
|
27183
|
-
if (user.role === "admin") {
|
|
27184
|
-
const gameExists = await this.deps.db.query.games.findFirst({
|
|
27185
|
-
where: eq(games.id, gameId),
|
|
27186
|
-
columns: { id: true }
|
|
27187
|
-
});
|
|
27188
|
-
if (!gameExists) {
|
|
27189
|
-
throw new NotFoundError("Game", gameId);
|
|
27190
|
-
}
|
|
27191
|
-
return;
|
|
27192
|
-
}
|
|
27193
27214
|
const db2 = this.deps.db;
|
|
27194
|
-
const
|
|
27195
|
-
where:
|
|
27215
|
+
const gameExists = await db2.query.games.findFirst({
|
|
27216
|
+
where: eq(games.id, gameId),
|
|
27196
27217
|
columns: { id: true }
|
|
27197
27218
|
});
|
|
27198
|
-
if (!
|
|
27219
|
+
if (!gameExists) {
|
|
27220
|
+
throw new NotFoundError("Game", gameId);
|
|
27221
|
+
}
|
|
27222
|
+
if (user.role === "admin") {
|
|
27223
|
+
return;
|
|
27224
|
+
}
|
|
27225
|
+
if (!await this.hasGameMembership(gameId, user.id)) {
|
|
27199
27226
|
throw new NotFoundError("Game", gameId);
|
|
27200
27227
|
}
|
|
27201
27228
|
}
|
|
@@ -27215,21 +27242,18 @@ var init_game_service = __esm(() => {
|
|
|
27215
27242
|
async validateDeveloperAccessBySlug(user, slug) {
|
|
27216
27243
|
this.validateDeveloperStatus(user);
|
|
27217
27244
|
const db2 = this.deps.db;
|
|
27218
|
-
if (user.role === "admin") {
|
|
27219
|
-
const game2 = await db2.query.games.findFirst({
|
|
27220
|
-
where: eq(games.slug, slug)
|
|
27221
|
-
});
|
|
27222
|
-
if (!game2) {
|
|
27223
|
-
throw new NotFoundError("Game", slug);
|
|
27224
|
-
}
|
|
27225
|
-
return game2;
|
|
27226
|
-
}
|
|
27227
27245
|
const game = await db2.query.games.findFirst({
|
|
27228
|
-
where:
|
|
27246
|
+
where: eq(games.slug, slug)
|
|
27229
27247
|
});
|
|
27230
27248
|
if (!game) {
|
|
27231
27249
|
throw new NotFoundError("Game", slug);
|
|
27232
27250
|
}
|
|
27251
|
+
if (user.role === "admin") {
|
|
27252
|
+
return game;
|
|
27253
|
+
}
|
|
27254
|
+
if (!await this.hasGameMembership(game.id, user.id)) {
|
|
27255
|
+
throw new NotFoundError("Game", slug);
|
|
27256
|
+
}
|
|
27233
27257
|
return game;
|
|
27234
27258
|
}
|
|
27235
27259
|
validateDeveloperStatus(user) {
|
|
@@ -34878,10 +34902,17 @@ class LogsService {
|
|
|
34878
34902
|
throw new AccessDeniedError("Must be an approved developer");
|
|
34879
34903
|
}
|
|
34880
34904
|
const game = await db2.query.games.findFirst({
|
|
34881
|
-
where:
|
|
34905
|
+
where: eq(games.slug, slug2),
|
|
34882
34906
|
columns: { id: true }
|
|
34883
34907
|
});
|
|
34884
34908
|
if (!game) {
|
|
34909
|
+
throw new NotFoundError("Game", slug2);
|
|
34910
|
+
}
|
|
34911
|
+
const membership = await db2.query.gameMembers.findFirst({
|
|
34912
|
+
where: and(eq(gameMembers.gameId, game.id), eq(gameMembers.userId, user.id)),
|
|
34913
|
+
columns: { id: true }
|
|
34914
|
+
});
|
|
34915
|
+
if (!membership) {
|
|
34885
34916
|
logger28.warn("Developer attempted access to unowned game logs", {
|
|
34886
34917
|
userId: user.id,
|
|
34887
34918
|
slug: slug2
|
|
@@ -93762,7 +93793,6 @@ async function seedCoreGames(db2) {
|
|
|
93762
93793
|
const coreGames = [
|
|
93763
93794
|
{
|
|
93764
93795
|
id: CORE_GAME_UUIDS.PLAYGROUND,
|
|
93765
|
-
developerId: DEMO_USERS.developer.id,
|
|
93766
93796
|
slug: "playground",
|
|
93767
93797
|
displayName: "Playground",
|
|
93768
93798
|
version: "local",
|
|
@@ -93779,6 +93809,11 @@ async function seedCoreGames(db2) {
|
|
|
93779
93809
|
for (const gameData of coreGames) {
|
|
93780
93810
|
try {
|
|
93781
93811
|
await db2.insert(games).values(gameData).onConflictDoNothing();
|
|
93812
|
+
await db2.insert(gameMembers).values({
|
|
93813
|
+
gameId: gameData.id,
|
|
93814
|
+
userId: DEMO_USERS.developer.id,
|
|
93815
|
+
role: "owner"
|
|
93816
|
+
}).onConflictDoNothing();
|
|
93782
93817
|
} catch (error2) {
|
|
93783
93818
|
logger37.error(`Error seeding core game '${gameData.slug}': ${error2}`);
|
|
93784
93819
|
}
|
|
@@ -93803,7 +93838,6 @@ async function seedCurrentProjectGame(db2, project) {
|
|
|
93803
93838
|
}
|
|
93804
93839
|
const gameRecord = {
|
|
93805
93840
|
id: desiredGameId ?? crypto.randomUUID(),
|
|
93806
|
-
developerId: DEMO_USERS.developer.id,
|
|
93807
93841
|
slug: project.slug,
|
|
93808
93842
|
displayName: project.displayName,
|
|
93809
93843
|
version: project.version,
|
|
@@ -93821,6 +93855,11 @@ async function seedCurrentProjectGame(db2, project) {
|
|
|
93821
93855
|
if (!newGame) {
|
|
93822
93856
|
throw new Error("Failed to create game record");
|
|
93823
93857
|
}
|
|
93858
|
+
await db2.insert(gameMembers).values({
|
|
93859
|
+
gameId: newGame.id,
|
|
93860
|
+
userId: DEMO_USERS.developer.id,
|
|
93861
|
+
role: "owner"
|
|
93862
|
+
}).onConflictDoNothing();
|
|
93824
93863
|
if (project.timebackCourses && project.timebackCourses.length > 0) {
|
|
93825
93864
|
await seedTimebackIntegrations(db2, newGame.id, project.timebackCourses);
|
|
93826
93865
|
}
|
|
@@ -94809,7 +94848,6 @@ var init_schemas2 = __esm(() => {
|
|
|
94809
94848
|
id: true,
|
|
94810
94849
|
slug: true,
|
|
94811
94850
|
createdAt: true,
|
|
94812
|
-
developerId: true,
|
|
94813
94851
|
version: true
|
|
94814
94852
|
}).refine((data) => {
|
|
94815
94853
|
if (data.gameType === "hosted" && data.deploymentUrl === null) {
|
|
@@ -96211,7 +96249,7 @@ var init_domain_controller = __esm(() => {
|
|
|
96211
96249
|
});
|
|
96212
96250
|
|
|
96213
96251
|
// ../api-core/src/controllers/game.controller.ts
|
|
96214
|
-
var logger48, list3,
|
|
96252
|
+
var logger48, list3, listAccessible, getSubjects, getById2, getBySlug, getManifest, upsertBySlug, remove3, games2;
|
|
96215
96253
|
var init_game_controller = __esm(() => {
|
|
96216
96254
|
init_esm();
|
|
96217
96255
|
init_schemas_index();
|
|
@@ -96224,9 +96262,9 @@ var init_game_controller = __esm(() => {
|
|
|
96224
96262
|
logger48.debug("Listing games", { userId: ctx.user.id });
|
|
96225
96263
|
return ctx.services.game.list(ctx.user);
|
|
96226
96264
|
});
|
|
96227
|
-
|
|
96228
|
-
logger48.debug("Listing
|
|
96229
|
-
return ctx.services.game.
|
|
96265
|
+
listAccessible = requireNonAnonymous(async (ctx) => {
|
|
96266
|
+
logger48.debug("Listing accessible games", { userId: ctx.user.id });
|
|
96267
|
+
return ctx.services.game.listAccessible(ctx.user);
|
|
96230
96268
|
});
|
|
96231
96269
|
getSubjects = requireNonAnonymous(async (ctx) => {
|
|
96232
96270
|
logger48.debug("Getting game subjects", { userId: ctx.user.id });
|
|
@@ -96299,7 +96337,7 @@ var init_game_controller = __esm(() => {
|
|
|
96299
96337
|
});
|
|
96300
96338
|
games2 = {
|
|
96301
96339
|
list: list3,
|
|
96302
|
-
|
|
96340
|
+
listAccessible,
|
|
96303
96341
|
getSubjects,
|
|
96304
96342
|
getById: getById2,
|
|
96305
96343
|
getManifest,
|
|
@@ -98420,6 +98458,7 @@ var init_crud = __esm(() => {
|
|
|
98420
98458
|
init_api();
|
|
98421
98459
|
gameCrudRouter = new Hono2;
|
|
98422
98460
|
gameCrudRouter.get("/", handle2(games2.list));
|
|
98461
|
+
gameCrudRouter.get("/accessible", handle2(games2.listAccessible));
|
|
98423
98462
|
gameCrudRouter.get("/:gameId{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}}/manifest", handle2(games2.getManifest));
|
|
98424
98463
|
gameCrudRouter.get("/:gameId{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}}", handle2(games2.getById));
|
|
98425
98464
|
gameCrudRouter.get("/:slug", handle2(games2.getBySlug));
|
|
@@ -98562,10 +98601,12 @@ var init_deploy = __esm(() => {
|
|
|
98562
98601
|
const db2 = ctx.db;
|
|
98563
98602
|
const existingGames = await db2.select().from(games).where(eq(games.slug, slug2));
|
|
98564
98603
|
const existingGame = existingGames[0];
|
|
98565
|
-
if (existingGame) {
|
|
98566
|
-
const
|
|
98567
|
-
|
|
98568
|
-
|
|
98604
|
+
if (existingGame && user.role !== "admin") {
|
|
98605
|
+
const membership = await db2.query.gameMembers.findFirst({
|
|
98606
|
+
where: and(eq(gameMembers.gameId, existingGame.id), eq(gameMembers.userId, user.id)),
|
|
98607
|
+
columns: { id: true }
|
|
98608
|
+
});
|
|
98609
|
+
if (!membership) {
|
|
98569
98610
|
return c2.json({
|
|
98570
98611
|
error: {
|
|
98571
98612
|
code: "FORBIDDEN",
|
|
@@ -98589,17 +98630,24 @@ var init_deploy = __esm(() => {
|
|
|
98589
98630
|
if (!body2.metadata?.displayName) {
|
|
98590
98631
|
return c2.json({ error: { code: "BAD_REQUEST", message: "Display name required for new game" } }, 400);
|
|
98591
98632
|
}
|
|
98592
|
-
const
|
|
98633
|
+
const gameValues = {
|
|
98593
98634
|
slug: slug2,
|
|
98594
98635
|
displayName: body2.metadata.displayName,
|
|
98595
98636
|
version: "1.0.0",
|
|
98596
98637
|
platform: body2.metadata.platform ?? "web",
|
|
98597
98638
|
gameType: "hosted",
|
|
98598
|
-
developerId: user.id,
|
|
98599
98639
|
deploymentUrl: `http://localhost:4321`,
|
|
98600
98640
|
metadata: body2.metadata.metadata ?? {}
|
|
98601
|
-
}
|
|
98602
|
-
game =
|
|
98641
|
+
};
|
|
98642
|
+
game = await db2.transaction(async (tx) => {
|
|
98643
|
+
const [inserted] = await tx.insert(games).values(gameValues).returning();
|
|
98644
|
+
await tx.insert(gameMembers).values({
|
|
98645
|
+
gameId: inserted.id,
|
|
98646
|
+
userId: user.id,
|
|
98647
|
+
role: "owner"
|
|
98648
|
+
});
|
|
98649
|
+
return inserted;
|
|
98650
|
+
});
|
|
98603
98651
|
}
|
|
98604
98652
|
let backendCode = body2.code;
|
|
98605
98653
|
if (!backendCode && body2.codeUploadToken) {
|
|
@@ -98678,13 +98726,19 @@ var init_deploy = __esm(() => {
|
|
|
98678
98726
|
const ctx = getSandboxContext();
|
|
98679
98727
|
const game = await ctx.db.query.games.findFirst({
|
|
98680
98728
|
where: eq(games.slug, slug2 ?? ""),
|
|
98681
|
-
columns: { id: true
|
|
98729
|
+
columns: { id: true }
|
|
98682
98730
|
});
|
|
98683
98731
|
if (!game) {
|
|
98684
98732
|
return c2.json({ error: { code: "NOT_FOUND", message: "Game not found" } }, 404);
|
|
98685
98733
|
}
|
|
98686
|
-
if (
|
|
98687
|
-
|
|
98734
|
+
if (user.role !== "admin") {
|
|
98735
|
+
const membership = await ctx.db.query.gameMembers.findFirst({
|
|
98736
|
+
where: and(eq(gameMembers.gameId, game.id), eq(gameMembers.userId, user.id)),
|
|
98737
|
+
columns: { id: true }
|
|
98738
|
+
});
|
|
98739
|
+
if (!membership) {
|
|
98740
|
+
return c2.json({ error: { code: "NOT_FOUND", message: "Game not found" } }, 404);
|
|
98741
|
+
}
|
|
98688
98742
|
}
|
|
98689
98743
|
const job = await ctx.db.query.gameDeployJobs.findFirst({
|
|
98690
98744
|
where: and(eq(gameDeployJobs.id, jobId), eq(gameDeployJobs.gameId, game.id))
|