@playcademy/sandbox 0.12.2-beta.5 → 0.12.2-beta.7
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 +53 -18
- package/dist/server.js +53 -18
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2517,7 +2517,7 @@ var package_default;
|
|
|
2517
2517
|
var init_package = __esm(() => {
|
|
2518
2518
|
package_default = {
|
|
2519
2519
|
name: "@playcademy/sandbox",
|
|
2520
|
-
version: "0.12.2-beta.
|
|
2520
|
+
version: "0.12.2-beta.7",
|
|
2521
2521
|
description: "Local development server for Playcademy game development",
|
|
2522
2522
|
type: "module",
|
|
2523
2523
|
exports: {
|
|
@@ -20895,21 +20895,25 @@ var init_table7 = __esm(() => {
|
|
|
20895
20895
|
});
|
|
20896
20896
|
|
|
20897
20897
|
// ../data/src/domains/embed/table.ts
|
|
20898
|
-
var embedPartners, embedLaunches, embedLaunchesRelations;
|
|
20898
|
+
var embedContextRoleEnum, embedPartners, embedLaunches, embedLaunchesRelations;
|
|
20899
20899
|
var init_table8 = __esm(() => {
|
|
20900
20900
|
init_drizzle_orm();
|
|
20901
20901
|
init_pg_core();
|
|
20902
20902
|
init_table5();
|
|
20903
20903
|
init_table3();
|
|
20904
|
+
embedContextRoleEnum = pgEnum("embed_context_role", ["host"]);
|
|
20904
20905
|
embedPartners = pgTable("embed_partners", {
|
|
20905
20906
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
20906
20907
|
name: text("name").notNull(),
|
|
20907
20908
|
clientId: text("client_id").notNull().unique(),
|
|
20909
|
+
handle: text("handle").unique(),
|
|
20908
20910
|
allowedOrigins: text("allowed_origins").array().notNull().default([]),
|
|
20909
20911
|
enabled: boolean("enabled").notNull().default(true),
|
|
20910
20912
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
20911
20913
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
20912
|
-
})
|
|
20914
|
+
}, (table) => [
|
|
20915
|
+
check("embed_partners_handle_check", sql`${table.handle} ~ '^[a-z0-9]([a-z0-9-]*[a-z0-9])?([.][a-z0-9]([a-z0-9-]*[a-z0-9])?)+$'`)
|
|
20916
|
+
]);
|
|
20913
20917
|
embedLaunches = pgTable("embed_launches", {
|
|
20914
20918
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
20915
20919
|
partnerId: uuid("partner_id").notNull().references(() => embedPartners.id, { onDelete: "restrict" }),
|
|
@@ -20918,6 +20922,8 @@ var init_table8 = __esm(() => {
|
|
|
20918
20922
|
parentOrigin: text("parent_origin").notNull(),
|
|
20919
20923
|
timebackCourseId: text("timeback_course_id"),
|
|
20920
20924
|
contextId: text("context_id"),
|
|
20925
|
+
contextRole: embedContextRoleEnum("context_role"),
|
|
20926
|
+
contextAttributes: jsonb("context_attributes").$type(),
|
|
20921
20927
|
passHash: text("pass_hash").unique(),
|
|
20922
20928
|
passExpiresAt: timestamp("pass_expires_at", { withTimezone: true }).notNull(),
|
|
20923
20929
|
redeemedAt: timestamp("redeemed_at", { withTimezone: true }),
|
|
@@ -20957,6 +20963,7 @@ __export(exports_tables_index, {
|
|
|
20957
20963
|
deploymentProviderEnum: () => deploymentProviderEnum,
|
|
20958
20964
|
deploymentTargetEnum: () => deploymentTargetEnum,
|
|
20959
20965
|
developerStatusEnum: () => developerStatusEnum,
|
|
20966
|
+
embedContextRoleEnum: () => embedContextRoleEnum,
|
|
20960
20967
|
embedLaunches: () => embedLaunches,
|
|
20961
20968
|
embedLaunchesRelations: () => embedLaunchesRelations,
|
|
20962
20969
|
embedPartners: () => embedPartners,
|
|
@@ -50824,16 +50831,29 @@ function isCanonicalOrigin(value) {
|
|
|
50824
50831
|
}
|
|
50825
50832
|
return url.protocol === "https:" || url.protocol === "http:" && isLocalHostname(url.hostname);
|
|
50826
50833
|
}
|
|
50827
|
-
var LOCAL_HOSTNAMES, EmbedOriginSchema, EmbedContextIdSchema, EmbedPartnerClientIdSchema, EnsureEmbedPartnerSchema, AddEmbedPartnerOriginSchema, CreateEmbedLaunchSchema, RedeemEmbedPassSchema;
|
|
50834
|
+
var LOCAL_HOSTNAMES, EmbedOriginSchema, EmbedContextIdSchema, MAX_CONTEXT_ATTRIBUTES = 10, EmbedContextAttributesSchema, EmbedContextSchema, EmbedPartnerClientIdSchema, EnsureEmbedPartnerSchema, AddEmbedPartnerOriginSchema, CreateEmbedLaunchSchema, RedeemEmbedPassSchema;
|
|
50828
50835
|
var init_schemas5 = __esm(() => {
|
|
50829
50836
|
init_esm();
|
|
50837
|
+
init_table8();
|
|
50830
50838
|
LOCAL_HOSTNAMES = new Set(["localhost", "127.0.0.1"]);
|
|
50831
50839
|
EmbedOriginSchema = stringType().refine(isCanonicalOrigin, {
|
|
50832
50840
|
message: "Origin must be a bare https origin like https://example.com, with no path or slash"
|
|
50833
50841
|
});
|
|
50834
50842
|
EmbedContextIdSchema = stringType().regex(/^[A-Za-z0-9._~-]{1,128}$/, {
|
|
50835
|
-
message: "
|
|
50836
|
-
});
|
|
50843
|
+
message: "context.id must be 1-128 URL-safe characters (letters, digits, . _ ~ -)"
|
|
50844
|
+
});
|
|
50845
|
+
EmbedContextAttributesSchema = recordType(stringType().regex(/^[A-Za-z][A-Za-z0-9_]{0,39}$/, {
|
|
50846
|
+
message: "context.attributes keys must be 1-40 characters: a letter, then letters, digits or _"
|
|
50847
|
+
}), stringType().regex(/^[A-Za-z0-9._~-]{1,100}$/, {
|
|
50848
|
+
message: "context.attributes values must be 1-100 URL-safe characters (letters, digits, . _ ~ -)"
|
|
50849
|
+
})).refine((attributes) => Object.keys(attributes).length <= MAX_CONTEXT_ATTRIBUTES, {
|
|
50850
|
+
message: `context.attributes takes at most ${MAX_CONTEXT_ATTRIBUTES} keys`
|
|
50851
|
+
});
|
|
50852
|
+
EmbedContextSchema = objectType({
|
|
50853
|
+
id: EmbedContextIdSchema,
|
|
50854
|
+
role: enumType(embedContextRoleEnum.enumValues).optional(),
|
|
50855
|
+
attributes: EmbedContextAttributesSchema.optional()
|
|
50856
|
+
}).strict();
|
|
50837
50857
|
EmbedPartnerClientIdSchema = stringType().trim().min(1).max(200);
|
|
50838
50858
|
EnsureEmbedPartnerSchema = objectType({
|
|
50839
50859
|
name: stringType().trim().min(1).max(100)
|
|
@@ -50846,7 +50866,7 @@ var init_schemas5 = __esm(() => {
|
|
|
50846
50866
|
gameSlug: stringType().trim().min(1).max(100),
|
|
50847
50867
|
parentOrigin: EmbedOriginSchema,
|
|
50848
50868
|
timebackCourseId: stringType().trim().min(1).max(200).optional(),
|
|
50849
|
-
|
|
50869
|
+
context: EmbedContextSchema.optional()
|
|
50850
50870
|
}).strict();
|
|
50851
50871
|
RedeemEmbedPassSchema = objectType({
|
|
50852
50872
|
pass: stringType().min(1).max(200),
|
|
@@ -95691,8 +95711,18 @@ var init_embed_util = __esm(() => {
|
|
|
95691
95711
|
async function scopedContextId(partnerId, contextId) {
|
|
95692
95712
|
return sha256Hex(`${partnerId}:${contextId}`);
|
|
95693
95713
|
}
|
|
95694
|
-
function launchTokenClaims(launch) {
|
|
95695
|
-
|
|
95714
|
+
function launchTokenClaims(launch, partner) {
|
|
95715
|
+
if (!launch.contextId) {
|
|
95716
|
+
return {};
|
|
95717
|
+
}
|
|
95718
|
+
return {
|
|
95719
|
+
context: {
|
|
95720
|
+
id: launch.contextId,
|
|
95721
|
+
...launch.contextRole && { role: launch.contextRole },
|
|
95722
|
+
...launch.contextAttributes && { attributes: launch.contextAttributes },
|
|
95723
|
+
...partner.handle && { partner: partner.handle }
|
|
95724
|
+
}
|
|
95725
|
+
};
|
|
95696
95726
|
}
|
|
95697
95727
|
|
|
95698
95728
|
class EmbedService {
|
|
@@ -95738,7 +95768,9 @@ class EmbedService {
|
|
|
95738
95768
|
gameId: game.id,
|
|
95739
95769
|
parentOrigin: input.parentOrigin,
|
|
95740
95770
|
timebackCourseId: input.timebackCourseId ?? null,
|
|
95741
|
-
contextId: input.
|
|
95771
|
+
contextId: input.context ? await scopedContextId(partner.id, input.context.id) : null,
|
|
95772
|
+
contextRole: input.context?.role ?? null,
|
|
95773
|
+
contextAttributes: input.context?.attributes ?? null,
|
|
95742
95774
|
passHash: await sha256Hex(pass),
|
|
95743
95775
|
passExpiresAt,
|
|
95744
95776
|
expiresAt: new Date(now + EMBED_LAUNCH_TTL_MS)
|
|
@@ -95749,12 +95781,16 @@ class EmbedService {
|
|
|
95749
95781
|
setAttributes({
|
|
95750
95782
|
"app.launch.id": launch.id,
|
|
95751
95783
|
"app.user.id": user.id,
|
|
95752
|
-
"app.embed.has_context": input.
|
|
95784
|
+
"app.embed.has_context": input.context !== undefined,
|
|
95785
|
+
...input.context?.role && { "app.embed.context_role": input.context.role },
|
|
95786
|
+
...input.context?.attributes && {
|
|
95787
|
+
"app.embed.context_attribute_keys": Object.keys(input.context.attributes).toSorted()
|
|
95788
|
+
}
|
|
95753
95789
|
});
|
|
95754
95790
|
return { launchId: launch.id, pass, passExpiresAt, gameSlug: game.slug };
|
|
95755
95791
|
}
|
|
95756
95792
|
async redeemLaunch(input, timeZone) {
|
|
95757
|
-
const launch = await this.consumePass(input);
|
|
95793
|
+
const { launch, partner } = await this.consumePass(input);
|
|
95758
95794
|
try {
|
|
95759
95795
|
const user = await this.deps.db.query.users.findFirst({
|
|
95760
95796
|
where: eq(users.id, launch.userId)
|
|
@@ -95765,7 +95801,7 @@ class EmbedService {
|
|
|
95765
95801
|
const [game, profile, token] = await Promise.all([
|
|
95766
95802
|
this.deps.getGameForStudent(launch.gameId, user),
|
|
95767
95803
|
this.deps.getGameProfile(user, launch.gameId, timeZone),
|
|
95768
|
-
this.deps.mintGameToken(launch.gameId, user.id, launchTokenClaims(launch))
|
|
95804
|
+
this.deps.mintGameToken(launch.gameId, user.id, launchTokenClaims(launch, partner))
|
|
95769
95805
|
]);
|
|
95770
95806
|
await this.deps.db.update(embedLaunches).set({ redeemedAt: new Date }).where(eq(embedLaunches.id, launch.id));
|
|
95771
95807
|
return {
|
|
@@ -95797,7 +95833,7 @@ class EmbedService {
|
|
|
95797
95833
|
if (outcome) {
|
|
95798
95834
|
throw new AccessDeniedError("This launch has ended");
|
|
95799
95835
|
}
|
|
95800
|
-
return this.deps.mintGameToken(launch.gameId, launch.userId, launchTokenClaims(launch));
|
|
95836
|
+
return this.deps.mintGameToken(launch.gameId, launch.userId, launchTokenClaims(launch, partner));
|
|
95801
95837
|
}
|
|
95802
95838
|
async endLaunch(launchId) {
|
|
95803
95839
|
await this.deps.db.update(embedLaunches).set({ endedAt: sql`coalesce(${embedLaunches.endedAt}, now())`, passHash: null }).where(eq(embedLaunches.id, launchId));
|
|
@@ -95817,18 +95853,17 @@ class EmbedService {
|
|
|
95817
95853
|
});
|
|
95818
95854
|
const partner = await this.deps.db.query.embedPartners.findFirst({
|
|
95819
95855
|
where: eq(embedPartners.id, launch.partnerId),
|
|
95820
|
-
columns: { enabled: true, allowedOrigins: true }
|
|
95856
|
+
columns: { enabled: true, allowedOrigins: true, handle: true }
|
|
95821
95857
|
});
|
|
95822
95858
|
const originMatches = launch.parentOrigin === input.parentOrigin;
|
|
95823
|
-
|
|
95824
|
-
if (!originMatches || !stillAllowed) {
|
|
95859
|
+
if (!originMatches || !partner || !canPartnerLaunch(partner, launch.parentOrigin)) {
|
|
95825
95860
|
await this.endLaunch(launch.id);
|
|
95826
95861
|
setAttributes({
|
|
95827
95862
|
"app.embed.outcome": originMatches ? "partner_disallowed" : "origin_mismatch"
|
|
95828
95863
|
});
|
|
95829
95864
|
throw new UnauthorizedError("This launch pass is not valid");
|
|
95830
95865
|
}
|
|
95831
|
-
return launch;
|
|
95866
|
+
return { launch, partner };
|
|
95832
95867
|
}
|
|
95833
95868
|
}
|
|
95834
95869
|
function renewalRefusal(launch, isAllowed) {
|
package/dist/server.js
CHANGED
|
@@ -697,7 +697,7 @@ var package_default;
|
|
|
697
697
|
var init_package = __esm(() => {
|
|
698
698
|
package_default = {
|
|
699
699
|
name: "@playcademy/sandbox",
|
|
700
|
-
version: "0.12.2-beta.
|
|
700
|
+
version: "0.12.2-beta.7",
|
|
701
701
|
description: "Local development server for Playcademy game development",
|
|
702
702
|
type: "module",
|
|
703
703
|
exports: {
|
|
@@ -19075,21 +19075,25 @@ var init_table7 = __esm(() => {
|
|
|
19075
19075
|
});
|
|
19076
19076
|
|
|
19077
19077
|
// ../data/src/domains/embed/table.ts
|
|
19078
|
-
var embedPartners, embedLaunches, embedLaunchesRelations;
|
|
19078
|
+
var embedContextRoleEnum, embedPartners, embedLaunches, embedLaunchesRelations;
|
|
19079
19079
|
var init_table8 = __esm(() => {
|
|
19080
19080
|
init_drizzle_orm();
|
|
19081
19081
|
init_pg_core();
|
|
19082
19082
|
init_table5();
|
|
19083
19083
|
init_table3();
|
|
19084
|
+
embedContextRoleEnum = pgEnum("embed_context_role", ["host"]);
|
|
19084
19085
|
embedPartners = pgTable("embed_partners", {
|
|
19085
19086
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
19086
19087
|
name: text("name").notNull(),
|
|
19087
19088
|
clientId: text("client_id").notNull().unique(),
|
|
19089
|
+
handle: text("handle").unique(),
|
|
19088
19090
|
allowedOrigins: text("allowed_origins").array().notNull().default([]),
|
|
19089
19091
|
enabled: boolean("enabled").notNull().default(true),
|
|
19090
19092
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
19091
19093
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
19092
|
-
})
|
|
19094
|
+
}, (table) => [
|
|
19095
|
+
check("embed_partners_handle_check", sql`${table.handle} ~ '^[a-z0-9]([a-z0-9-]*[a-z0-9])?([.][a-z0-9]([a-z0-9-]*[a-z0-9])?)+$'`)
|
|
19096
|
+
]);
|
|
19093
19097
|
embedLaunches = pgTable("embed_launches", {
|
|
19094
19098
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
19095
19099
|
partnerId: uuid("partner_id").notNull().references(() => embedPartners.id, { onDelete: "restrict" }),
|
|
@@ -19098,6 +19102,8 @@ var init_table8 = __esm(() => {
|
|
|
19098
19102
|
parentOrigin: text("parent_origin").notNull(),
|
|
19099
19103
|
timebackCourseId: text("timeback_course_id"),
|
|
19100
19104
|
contextId: text("context_id"),
|
|
19105
|
+
contextRole: embedContextRoleEnum("context_role"),
|
|
19106
|
+
contextAttributes: jsonb("context_attributes").$type(),
|
|
19101
19107
|
passHash: text("pass_hash").unique(),
|
|
19102
19108
|
passExpiresAt: timestamp("pass_expires_at", { withTimezone: true }).notNull(),
|
|
19103
19109
|
redeemedAt: timestamp("redeemed_at", { withTimezone: true }),
|
|
@@ -19137,6 +19143,7 @@ __export(exports_tables_index, {
|
|
|
19137
19143
|
deploymentProviderEnum: () => deploymentProviderEnum,
|
|
19138
19144
|
deploymentTargetEnum: () => deploymentTargetEnum,
|
|
19139
19145
|
developerStatusEnum: () => developerStatusEnum,
|
|
19146
|
+
embedContextRoleEnum: () => embedContextRoleEnum,
|
|
19140
19147
|
embedLaunches: () => embedLaunches,
|
|
19141
19148
|
embedLaunchesRelations: () => embedLaunchesRelations,
|
|
19142
19149
|
embedPartners: () => embedPartners,
|
|
@@ -49004,16 +49011,29 @@ function isCanonicalOrigin(value) {
|
|
|
49004
49011
|
}
|
|
49005
49012
|
return url.protocol === "https:" || url.protocol === "http:" && isLocalHostname(url.hostname);
|
|
49006
49013
|
}
|
|
49007
|
-
var LOCAL_HOSTNAMES, EmbedOriginSchema, EmbedContextIdSchema, EmbedPartnerClientIdSchema, EnsureEmbedPartnerSchema, AddEmbedPartnerOriginSchema, CreateEmbedLaunchSchema, RedeemEmbedPassSchema;
|
|
49014
|
+
var LOCAL_HOSTNAMES, EmbedOriginSchema, EmbedContextIdSchema, MAX_CONTEXT_ATTRIBUTES = 10, EmbedContextAttributesSchema, EmbedContextSchema, EmbedPartnerClientIdSchema, EnsureEmbedPartnerSchema, AddEmbedPartnerOriginSchema, CreateEmbedLaunchSchema, RedeemEmbedPassSchema;
|
|
49008
49015
|
var init_schemas5 = __esm(() => {
|
|
49009
49016
|
init_esm();
|
|
49017
|
+
init_table8();
|
|
49010
49018
|
LOCAL_HOSTNAMES = new Set(["localhost", "127.0.0.1"]);
|
|
49011
49019
|
EmbedOriginSchema = stringType().refine(isCanonicalOrigin, {
|
|
49012
49020
|
message: "Origin must be a bare https origin like https://example.com, with no path or slash"
|
|
49013
49021
|
});
|
|
49014
49022
|
EmbedContextIdSchema = stringType().regex(/^[A-Za-z0-9._~-]{1,128}$/, {
|
|
49015
|
-
message: "
|
|
49016
|
-
});
|
|
49023
|
+
message: "context.id must be 1-128 URL-safe characters (letters, digits, . _ ~ -)"
|
|
49024
|
+
});
|
|
49025
|
+
EmbedContextAttributesSchema = recordType(stringType().regex(/^[A-Za-z][A-Za-z0-9_]{0,39}$/, {
|
|
49026
|
+
message: "context.attributes keys must be 1-40 characters: a letter, then letters, digits or _"
|
|
49027
|
+
}), stringType().regex(/^[A-Za-z0-9._~-]{1,100}$/, {
|
|
49028
|
+
message: "context.attributes values must be 1-100 URL-safe characters (letters, digits, . _ ~ -)"
|
|
49029
|
+
})).refine((attributes) => Object.keys(attributes).length <= MAX_CONTEXT_ATTRIBUTES, {
|
|
49030
|
+
message: `context.attributes takes at most ${MAX_CONTEXT_ATTRIBUTES} keys`
|
|
49031
|
+
});
|
|
49032
|
+
EmbedContextSchema = objectType({
|
|
49033
|
+
id: EmbedContextIdSchema,
|
|
49034
|
+
role: enumType(embedContextRoleEnum.enumValues).optional(),
|
|
49035
|
+
attributes: EmbedContextAttributesSchema.optional()
|
|
49036
|
+
}).strict();
|
|
49017
49037
|
EmbedPartnerClientIdSchema = stringType().trim().min(1).max(200);
|
|
49018
49038
|
EnsureEmbedPartnerSchema = objectType({
|
|
49019
49039
|
name: stringType().trim().min(1).max(100)
|
|
@@ -49026,7 +49046,7 @@ var init_schemas5 = __esm(() => {
|
|
|
49026
49046
|
gameSlug: stringType().trim().min(1).max(100),
|
|
49027
49047
|
parentOrigin: EmbedOriginSchema,
|
|
49028
49048
|
timebackCourseId: stringType().trim().min(1).max(200).optional(),
|
|
49029
|
-
|
|
49049
|
+
context: EmbedContextSchema.optional()
|
|
49030
49050
|
}).strict();
|
|
49031
49051
|
RedeemEmbedPassSchema = objectType({
|
|
49032
49052
|
pass: stringType().min(1).max(200),
|
|
@@ -93871,8 +93891,18 @@ var init_embed_util = __esm(() => {
|
|
|
93871
93891
|
async function scopedContextId(partnerId, contextId) {
|
|
93872
93892
|
return sha256Hex(`${partnerId}:${contextId}`);
|
|
93873
93893
|
}
|
|
93874
|
-
function launchTokenClaims(launch) {
|
|
93875
|
-
|
|
93894
|
+
function launchTokenClaims(launch, partner) {
|
|
93895
|
+
if (!launch.contextId) {
|
|
93896
|
+
return {};
|
|
93897
|
+
}
|
|
93898
|
+
return {
|
|
93899
|
+
context: {
|
|
93900
|
+
id: launch.contextId,
|
|
93901
|
+
...launch.contextRole && { role: launch.contextRole },
|
|
93902
|
+
...launch.contextAttributes && { attributes: launch.contextAttributes },
|
|
93903
|
+
...partner.handle && { partner: partner.handle }
|
|
93904
|
+
}
|
|
93905
|
+
};
|
|
93876
93906
|
}
|
|
93877
93907
|
|
|
93878
93908
|
class EmbedService {
|
|
@@ -93918,7 +93948,9 @@ class EmbedService {
|
|
|
93918
93948
|
gameId: game.id,
|
|
93919
93949
|
parentOrigin: input.parentOrigin,
|
|
93920
93950
|
timebackCourseId: input.timebackCourseId ?? null,
|
|
93921
|
-
contextId: input.
|
|
93951
|
+
contextId: input.context ? await scopedContextId(partner.id, input.context.id) : null,
|
|
93952
|
+
contextRole: input.context?.role ?? null,
|
|
93953
|
+
contextAttributes: input.context?.attributes ?? null,
|
|
93922
93954
|
passHash: await sha256Hex(pass),
|
|
93923
93955
|
passExpiresAt,
|
|
93924
93956
|
expiresAt: new Date(now + EMBED_LAUNCH_TTL_MS)
|
|
@@ -93929,12 +93961,16 @@ class EmbedService {
|
|
|
93929
93961
|
setAttributes({
|
|
93930
93962
|
"app.launch.id": launch.id,
|
|
93931
93963
|
"app.user.id": user.id,
|
|
93932
|
-
"app.embed.has_context": input.
|
|
93964
|
+
"app.embed.has_context": input.context !== undefined,
|
|
93965
|
+
...input.context?.role && { "app.embed.context_role": input.context.role },
|
|
93966
|
+
...input.context?.attributes && {
|
|
93967
|
+
"app.embed.context_attribute_keys": Object.keys(input.context.attributes).toSorted()
|
|
93968
|
+
}
|
|
93933
93969
|
});
|
|
93934
93970
|
return { launchId: launch.id, pass, passExpiresAt, gameSlug: game.slug };
|
|
93935
93971
|
}
|
|
93936
93972
|
async redeemLaunch(input, timeZone) {
|
|
93937
|
-
const launch = await this.consumePass(input);
|
|
93973
|
+
const { launch, partner } = await this.consumePass(input);
|
|
93938
93974
|
try {
|
|
93939
93975
|
const user = await this.deps.db.query.users.findFirst({
|
|
93940
93976
|
where: eq(users.id, launch.userId)
|
|
@@ -93945,7 +93981,7 @@ class EmbedService {
|
|
|
93945
93981
|
const [game, profile, token] = await Promise.all([
|
|
93946
93982
|
this.deps.getGameForStudent(launch.gameId, user),
|
|
93947
93983
|
this.deps.getGameProfile(user, launch.gameId, timeZone),
|
|
93948
|
-
this.deps.mintGameToken(launch.gameId, user.id, launchTokenClaims(launch))
|
|
93984
|
+
this.deps.mintGameToken(launch.gameId, user.id, launchTokenClaims(launch, partner))
|
|
93949
93985
|
]);
|
|
93950
93986
|
await this.deps.db.update(embedLaunches).set({ redeemedAt: new Date }).where(eq(embedLaunches.id, launch.id));
|
|
93951
93987
|
return {
|
|
@@ -93977,7 +94013,7 @@ class EmbedService {
|
|
|
93977
94013
|
if (outcome) {
|
|
93978
94014
|
throw new AccessDeniedError("This launch has ended");
|
|
93979
94015
|
}
|
|
93980
|
-
return this.deps.mintGameToken(launch.gameId, launch.userId, launchTokenClaims(launch));
|
|
94016
|
+
return this.deps.mintGameToken(launch.gameId, launch.userId, launchTokenClaims(launch, partner));
|
|
93981
94017
|
}
|
|
93982
94018
|
async endLaunch(launchId) {
|
|
93983
94019
|
await this.deps.db.update(embedLaunches).set({ endedAt: sql`coalesce(${embedLaunches.endedAt}, now())`, passHash: null }).where(eq(embedLaunches.id, launchId));
|
|
@@ -93997,18 +94033,17 @@ class EmbedService {
|
|
|
93997
94033
|
});
|
|
93998
94034
|
const partner = await this.deps.db.query.embedPartners.findFirst({
|
|
93999
94035
|
where: eq(embedPartners.id, launch.partnerId),
|
|
94000
|
-
columns: { enabled: true, allowedOrigins: true }
|
|
94036
|
+
columns: { enabled: true, allowedOrigins: true, handle: true }
|
|
94001
94037
|
});
|
|
94002
94038
|
const originMatches = launch.parentOrigin === input.parentOrigin;
|
|
94003
|
-
|
|
94004
|
-
if (!originMatches || !stillAllowed) {
|
|
94039
|
+
if (!originMatches || !partner || !canPartnerLaunch(partner, launch.parentOrigin)) {
|
|
94005
94040
|
await this.endLaunch(launch.id);
|
|
94006
94041
|
setAttributes({
|
|
94007
94042
|
"app.embed.outcome": originMatches ? "partner_disallowed" : "origin_mismatch"
|
|
94008
94043
|
});
|
|
94009
94044
|
throw new UnauthorizedError("This launch pass is not valid");
|
|
94010
94045
|
}
|
|
94011
|
-
return launch;
|
|
94046
|
+
return { launch, partner };
|
|
94012
94047
|
}
|
|
94013
94048
|
}
|
|
94014
94049
|
function renewalRefusal(launch, isAllowed) {
|