@@ -5,7 +5,7 @@ import path from "node:path"
55import * as p from "@clack/prompts"
66
77import { GameNotFoundError , uploadFiles } from "../util/api.js"
8- import { getAPIURL , getToken } from "../util/config.js"
8+ import { type TokenEntry , findTokensForTeam , getTokens , resolveServerForTeam , sourceToURL } from "../util/config.js"
99import { createUserGame } from "../util/createGame.js"
1010import { discoverGames , type DiscoveredGame } from "../util/discoverGames.js"
1111
@@ -16,6 +16,7 @@ type UploadOptions = {
1616type GameSuccess = {
1717 ok : true
1818 slug : string
19+ source : string
1920 fileCount : number
2021 totalBytes : number
2122 versionID : string
@@ -33,8 +34,8 @@ type GameResult = GameSuccess | GameFailure
3334/** Uploads game build artifacts to Puzzmo by discovering puzzmo.json files in `dir` */
3435export const upload = async ( dir : string , options : UploadOptions = { } ) => {
3536 const { verbose = false } = options
36- const token = getToken ( )
37- if ( ! token ) {
37+
38+ if ( getTokens ( ) . length === 0 ) {
3839 console . error ( "Not logged in. Run `puzzmo login <token>` or set PUZZMO_TOKEN." )
3940 process . exit ( 1 )
4041 }
@@ -70,33 +71,47 @@ export const upload = async (dir: string, options: UploadOptions = {}) => {
7071 if ( description ) console . log ( `\nMessage: ${ description } ` )
7172 if ( repoURL ) console . log ( `Repo: ${ repoURL } ` )
7273
73- const apiURL = getAPIURL ( )
74- const defaultURL = "https://api.puzzmo.com"
75- if ( apiURL !== defaultURL ) console . log ( `API: ${ apiURL } ` )
76-
7774 const results : GameResult [ ] = [ ]
7875 for ( const err of discoveryErrors ) {
7976 results . push ( { ok : false , slug : err . slug ?? path . relative ( rootDir , err . puzzmoJsonPath ) , error : err . errors . join ( "; " ) } )
8077 }
8178
8279 for ( let i = 0 ; i < games . length ; i ++ ) {
8380 const game = games [ i ]
84- console . log ( `\n[${ i + 1 } /${ games . length } ] Uploading ${ game . puzzmoFile . game . slug } ` )
81+ const slug = game . puzzmoFile . game . slug
82+ console . log ( `\n[${ i + 1 } /${ games . length } ] Uploading ${ slug } ` )
83+
84+ const teamID = game . puzzmoFile . game . teamID
85+ const credential = await resolveServerForTeam ( teamID )
86+ if ( ! credential ) {
87+ const matches = findTokensForTeam ( teamID )
88+ const message =
89+ matches . length === 0
90+ ? `No saved token for team ${ teamID } . Run \`puzzmo login <token>\` (use \`--source\` if the token is for a non-default server).`
91+ : `Token for team ${ teamID } is registered against ${ matches . map ( ( m ) => m . source ) . join ( ", " ) } but none of those servers are reachable.`
92+ console . error ( ` ${ message } ` )
93+ results . push ( { ok : false , slug, error : message } )
94+ continue
95+ }
96+
97+ const apiURL = sourceToURL ( credential . source )
98+ console . log ( ` Server: ${ credential . source } ` )
99+
85100 try {
86101 let result : GameSuccess
87102 try {
88- result = await uploadOneGame ( game , { token , sha, description, repoURL, verbose, rootDir } )
103+ result = await uploadOneGame ( game , { credential , apiURL , sha, description, repoURL, verbose, rootDir } )
89104 } catch ( e ) {
90105 if ( ! ( e instanceof GameNotFoundError ) ) throw e
91- const created = await maybeCreateMissingGame ( e , game , { teamAccessToken : token } )
106+ const created = await maybeCreateMissingGame ( e , game , { apiURL , teamAccessToken : credential . token } )
92107 if ( ! created ) throw e
93- result = await uploadOneGame ( game , { token , sha, description, repoURL, verbose, rootDir } )
108+ result = await uploadOneGame ( game , { credential , apiURL , sha, description, repoURL, verbose, rootDir } )
94109 }
95110 results . push ( result )
96111 } catch ( e ) {
97112 const message = e instanceof Error ? e . message : String ( e )
98113 console . error ( ` Failed: ${ message } ` )
99- results . push ( { ok : false , slug : game . puzzmoFile . game . slug , error : message } )
114+ results . push ( { ok : false , slug, error : message } )
100115 }
101116 }
102117
@@ -107,7 +122,8 @@ export const upload = async (dir: string, options: UploadOptions = {}) => {
107122}
108123
109124type UploadOneOptions = {
110- token : string
125+ credential : TokenEntry
126+ apiURL : string
111127 sha : string
112128 description : string | null
113129 repoURL : string | null
@@ -117,7 +133,7 @@ type UploadOneOptions = {
117133
118134/** Uploads a single discovered game; throws on failure */
119135const uploadOneGame = async ( game : DiscoveredGame , opts : UploadOneOptions ) : Promise < GameSuccess > => {
120- const { token , sha, description, repoURL, verbose, rootDir } = opts
136+ const { credential , apiURL , sha, description, repoURL, verbose, rootDir } = opts
121137 const { puzzmoFile, distDir } = game
122138 const gameSlug = puzzmoFile . game . slug
123139
@@ -130,7 +146,8 @@ const uploadOneGame = async (game: DiscoveredGame, opts: UploadOneOptions): Prom
130146 console . log ( ` ${ files . length } file(s), ${ formatBytes ( totalBytes ) } total (sha ${ sha . slice ( 0 , 8 ) } )` )
131147
132148 const result = await uploadFiles (
133- token ,
149+ apiURL ,
150+ credential . token ,
134151 gameSlug ,
135152 sha ,
136153 files ,
@@ -146,6 +163,7 @@ const uploadOneGame = async (game: DiscoveredGame, opts: UploadOneOptions): Prom
146163 return {
147164 ok : true ,
148165 slug : gameSlug ,
166+ source : credential . source ,
149167 fileCount : files . length ,
150168 totalBytes,
151169 versionID : result . versionID ,
@@ -159,7 +177,7 @@ const printReport = (results: GameResult[]) => {
159177 const successes = results . filter ( ( r ) : r is GameSuccess => r . ok )
160178 const failures = results . filter ( ( r ) : r is GameFailure => ! r . ok )
161179 for ( const r of successes ) {
162- console . log ( ` OK ${ r . slug . padEnd ( 24 ) } ${ formatBytes ( r . totalBytes ) . padStart ( 8 ) } ${ r . versionID } ` )
180+ console . log ( ` OK ${ r . slug . padEnd ( 24 ) } ${ formatBytes ( r . totalBytes ) . padStart ( 8 ) } ${ r . source . padEnd ( 20 ) } ${ r . versionID } ` )
163181 }
164182 for ( const r of failures ) {
165183 console . log ( ` FAIL ${ r . slug . padEnd ( 24 ) } ${ r . error } ` )
@@ -241,7 +259,7 @@ const hashGames = (games: DiscoveredGame[]): string => {
241259const maybeCreateMissingGame = async (
242260 err : GameNotFoundError ,
243261 game : DiscoveredGame ,
244- opts : { teamAccessToken : string } ,
262+ opts : { apiURL : string ; teamAccessToken : string } ,
245263) : Promise < boolean > => {
246264 const slug = err . slug
247265 const displayName = game . puzzmoFile . game . displayName
@@ -258,7 +276,7 @@ const maybeCreateMissingGame = async (
258276 if ( p . isCancel ( consent ) || ! consent ) return false
259277
260278 try {
261- const created = await createUserGame ( { displayName, teamAccessToken : opts . teamAccessToken } )
279+ const created = await createUserGame ( { apiURL : opts . apiURL , displayName, teamAccessToken : opts . teamAccessToken } )
262280 console . log ( ` Created game ${ created . slug } (${ created . id } )` )
263281 if ( created . slug !== slug ) {
264282 console . warn ( ` Server picked slug "${ created . slug } " but your puzzmo.json uses "${ slug } ". Update puzzmo.json before re-uploading.` )
0 commit comments