shokupan 0.16.4 → 0.16.6

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.
@@ -6690,7 +6690,7 @@ class Dashboard {
6690
6690
  }
6691
6691
  this.metrics.logs.push(requestData);
6692
6692
  const idString = `req_${Date.now()}_${nanoid()}`;
6693
- this.db.upsert("request", idString, {
6693
+ this.db?.upsert("request", idString, {
6694
6694
  ...requestData,
6695
6695
  id: idString
6696
6696
  }).catch((e) => this[$appRoot]?.logger?.error("Dashboard", "Failed to save outbound request", { error: e }));
@@ -9770,6 +9770,7 @@ class Shokupan extends ShokupanRouter {
9770
9770
  startupHooks = [];
9771
9771
  plugins = [];
9772
9772
  specAvailableHooks = [];
9773
+ pluginInitPromises = [];
9773
9774
  get db() {
9774
9775
  return this.datastore;
9775
9776
  }
@@ -9882,24 +9883,24 @@ class Shokupan extends ShokupanRouter {
9882
9883
  try {
9883
9884
  switch (adapterName) {
9884
9885
  case "sqlite": {
9885
- const { SqliteAdapter } = await import("./sqlite-Db0vf9zn.js");
9886
+ const { SqliteAdapter } = await import("./sqlite-D6NnHuca.js");
9886
9887
  this.datastore = new SqliteAdapter(options);
9887
9888
  break;
9888
9889
  }
9889
9890
  case "level": {
9890
- const { LevelAdapter } = await import("./level-R8z-VF5o.js");
9891
+ const { LevelAdapter } = await import("./level-BEizp2x6.js");
9891
9892
  this.datastore = new LevelAdapter(options);
9892
9893
  break;
9893
9894
  }
9894
9895
  case "surrealdb": {
9895
- const { SurrealAdapter } = await import("./surreal-CxhSJpgW.js");
9896
+ const { SurrealAdapter } = await import("./surreal-CNJ26Wpt.js");
9896
9897
  const legacyConfig = this.applicationConfig.surreal || {};
9897
9898
  const effectiveOptions = { ...legacyConfig, ...options };
9898
9899
  this.datastore = new SurrealAdapter(effectiveOptions);
9899
9900
  break;
9900
9901
  }
9901
9902
  default: {
9902
- const { SurrealAdapter } = await import("./surreal-CxhSJpgW.js");
9903
+ const { SurrealAdapter } = await import("./surreal-CNJ26Wpt.js");
9903
9904
  const legacy = this.applicationConfig.surreal;
9904
9905
  this.datastore = new SurrealAdapter(options || legacy || {});
9905
9906
  }
@@ -9935,11 +9936,16 @@ class Shokupan extends ShokupanRouter {
9935
9936
  }
9936
9937
  /**
9937
9938
  * Registers a plugin.
9939
+ * This returns a promise that resolves when the plugin is initialized. You do not
9940
+ * need to await it unless you want to run code specifically after the plugin is initialized.
9941
+ * Shokupan automatically awaits plugin initialization promises when calling listen().
9938
9942
  */
9939
9943
  async register(plugin, options) {
9940
9944
  this.plugins.push(plugin);
9941
9945
  try {
9942
- await plugin.onInit(this, options);
9946
+ const promise = plugin.onInit(this, options);
9947
+ this.pluginInitPromises.push(promise);
9948
+ await promise;
9943
9949
  } catch (err) {
9944
9950
  this.logger?.error("Shokupan", "Failed to initialize plugin", { error: err });
9945
9951
  throw err;
@@ -10105,6 +10111,7 @@ class Shokupan extends ShokupanRouter {
10105
10111
  */
10106
10112
  async listen(port, callback) {
10107
10113
  this.httpServer = new ShokupanServer(this);
10114
+ await Promise.allSettled(this.pluginInitPromises);
10108
10115
  this.server = await this.httpServer.listen(port);
10109
10116
  const protocol = this.applicationConfig.tls || this.applicationConfig.development ? "https" : "http";
10110
10117
  const url = `${protocol}://${this.applicationConfig.hostname}:${this.applicationConfig.port}`;
@@ -10559,6 +10566,21 @@ class AuthPlugin extends ShokupanRouter {
10559
10566
  return jwt;
10560
10567
  }
10561
10568
  init() {
10569
+ this.get("/auth/me", async (ctx) => {
10570
+ const cookieHeader = ctx.req.headers.get("Cookie");
10571
+ const token = cookieHeader?.match(/auth_token=([^;]+)/)?.[1];
10572
+ if (!token) return ctx.json({ error: "Unauthenticated" }, 401);
10573
+ try {
10574
+ const { payload } = await this.jose.jwtVerify(token, this.secret);
10575
+ return ctx.json(payload);
10576
+ } catch {
10577
+ return ctx.json({ error: "Invalid or expired token" }, 401);
10578
+ }
10579
+ });
10580
+ this.post("/auth/logout", (ctx) => {
10581
+ ctx.set("Set-Cookie", "auth_token=; Path=/; HttpOnly; Max-Age=0; SameSite=Lax");
10582
+ return ctx.json({ ok: true });
10583
+ });
10562
10584
  const { generateState, generateCodeVerifier, GitHub, Google, MicrosoftEntraId, Apple, Auth0, Okta, OAuth2Client } = this.arctic;
10563
10585
  const providerEntries = Object.entries(this.authConfig.providers);
10564
10586
  for (let i = 0; i < providerEntries.length; i++) {
@@ -10596,6 +10618,7 @@ class AuthPlugin extends ShokupanRouter {
10596
10618
  const url = new URL(ctx.req.url);
10597
10619
  const code = url.searchParams.get("code");
10598
10620
  const state = url.searchParams.get("state");
10621
+ console.log("== OAuth Callback Hit ==", { providerName, code, state });
10599
10622
  const cookieHeader = ctx.req.headers.get("Cookie");
10600
10623
  const storedState = cookieHeader?.match(/oauth_state=([^;]+)/)?.[1];
10601
10624
  const storedVerifier = cookieHeader?.match(/oauth_verifier=([^;]+)/)?.[1];
@@ -10619,17 +10642,38 @@ class AuthPlugin extends ShokupanRouter {
10619
10642
  if (!providerConfig.tokenUrl) return ctx.text("Config error: tokenUrl required for oauth2", 500);
10620
10643
  tokens = await provider.validateAuthorizationCode(providerConfig.tokenUrl, code, null);
10621
10644
  }
10622
- const accessToken = tokens.accessToken || tokens.access_token;
10645
+ const accessToken = typeof tokens.accessToken === "function" ? tokens.accessToken() : tokens.accessToken || tokens.access_token;
10646
+ try {
10647
+ if (typeof tokens.idToken === "function") {
10648
+ idToken = tokens.idToken();
10649
+ } else if (tokens.idToken) {
10650
+ idToken = tokens.idToken;
10651
+ }
10652
+ } catch (e) {
10653
+ }
10623
10654
  const user = await this.fetchUser(providerName, accessToken, providerConfig, idToken);
10624
10655
  if (this.authConfig.onSuccess) {
10625
10656
  const res = await this.authConfig.onSuccess(user, ctx);
10626
10657
  if (res) return res;
10627
10658
  }
10628
10659
  const jwt = await this.createSession(user, ctx);
10660
+ if (this.authConfig.successRedirect) {
10661
+ return ctx.redirect(this.authConfig.successRedirect);
10662
+ }
10629
10663
  return ctx.json({ token: jwt, user });
10630
10664
  } catch (e) {
10665
+ console.error("Auth Exception:", e);
10666
+ let extradata = "";
10667
+ try {
10668
+ if (e && e.response) extradata = " | Body: " + await e.response.text();
10669
+ } catch {
10670
+ }
10631
10671
  ctx.app?.logger?.error("Auth", "Authentication failed", e);
10632
- return ctx.text("Authentication failed. Please try again.", 500);
10672
+ return ctx.text(`Authentication failed.
10673
+ Error: ${e?.message ?? String(e)}${extradata}
10674
+
10675
+ Stack:
10676
+ ${e?.stack}`, 500);
10633
10677
  }
10634
10678
  });
10635
10679
  }
@@ -10638,7 +10682,10 @@ class AuthPlugin extends ShokupanRouter {
10638
10682
  let user = { id: "unknown", provider };
10639
10683
  if (provider === "github") {
10640
10684
  const res = await fetch("https://api.github.com/user", {
10641
- headers: { Authorization: `Bearer ${token}` }
10685
+ headers: {
10686
+ Authorization: `Bearer ${token}`,
10687
+ "User-Agent": "Shokupan-Auth/1.0"
10688
+ }
10642
10689
  });
10643
10690
  const data = await res.json();
10644
10691
  user = {
@@ -13025,4 +13072,4 @@ export {
13025
13072
  OnOpen as y,
13026
13073
  OnEvent as z
13027
13074
  };
13028
- //# sourceMappingURL=index-Bb1U2B0-.js.map
13075
+ //# sourceMappingURL=index-DbBJL3MK.js.map