diff --git a/AGENTS.md b/AGENTS.md index eac4bbd..9bf8de2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,7 +13,7 @@ This application uses Cloudflare Developer Platform, including Workers and Durab - TypeScript strict mode - Single quotes, trailing comma -- Use prettier for formatting after each prompt +- Run `npm run format` to format the code after each prompt ## Script rules @@ -22,4 +22,10 @@ This application uses Cloudflare Developer Platform, including Workers and Durab ## Worker implementation -- Internal worker routes all start with ${usersPath}, make sure to always prefix them +- Internal worker routes all start with `${usersPath}`, make sure to always prefix them +- Never override `.env` and `.dev.vars` files + +## API + +- All non-admin API paths start with `/${usersPath}/api/` +- All admin API paths start with `/${usersPath}/admin/api/` diff --git a/public/users/admin/index.html b/public/users/admin/index.html new file mode 100644 index 0000000..27f1731 --- /dev/null +++ b/public/users/admin/index.html @@ -0,0 +1,729 @@ + + + + + + Admin Dashboard + + + + + + + + +

StartupAPI Admin

+ +
+

Users

+ + + + + + + + + + + + + + + +
IDNameEmailProviderCreatedActions
+
+ +
+
+

Accounts

+ +
+ + + + + + + + + + + + + + + + +
IDNameStatusPlanMembersCreatedActions
+
+ +
+ + + +

Edit User

+ +
+ + +
+
+ + +
+ +
+ + + +

User Memberships

+ + + + + + + + + + + + + +
AccountRoleCurrentActions
+ +
+ + + +

Edit Account

+ +
+ + +
+
+ + +
+
+ + +
+ +
+ + + +

Create New Account

+
+ + +
+
+ + +
+
+ + +
+ +
+ + + +

Manage Members

+ + +
+

Add Member

+
+ + + +
+
+ +

Current Members

+ + + + + + + + + + + + +
UserRoleJoinedActions
+ + +
+ + + + diff --git a/public/users/power-strip.js b/public/users/power-strip.js index 8519ea4..d349510 100644 --- a/public/users/power-strip.js +++ b/public/users/power-strip.js @@ -34,13 +34,17 @@ class PowerStrip extends HTMLElement { async fetchUser() { try { - const res = await fetch(`${this.basePath}/me`); + const res = await fetch(`${this.basePath}/api/me`); if (res.ok) { const data = await res.json(); if (data.valid) { - this.user = data.profile; + this.user = { + profile: data.profile, + is_admin: data.is_admin, + is_impersonated: data.is_impersonated, + }; // Fetch accounts if logged in - const accountsRes = await fetch(`${this.basePath}/me/accounts`); + const accountsRes = await fetch(`${this.basePath}/api/me/accounts`); if (accountsRes.ok) { this.accounts = await accountsRes.json(); } @@ -53,7 +57,7 @@ class PowerStrip extends HTMLElement { async switchAccount(accountId) { try { - const res = await fetch(`${this.basePath}/me/accounts/switch`, { + const res = await fetch(`${this.basePath}/api/me/accounts/switch`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -122,22 +126,40 @@ class PowerStrip extends HTMLElement { if (providers.length > 0 && providers[0] !== '') { if (this.user) { - const providerIcon = this.getProviderIcon(this.user.provider); - const currentAccount = this.accounts.find(a => a.is_current) || (this.accounts.length > 0 ? this.accounts[0] : null); - const accountName = currentAccount ? currentAccount.name : 'No Account'; - + const providerIcon = this.getProviderIcon(this.user.profile.provider); + const currentAccount = this.accounts.find((a) => a.is_current) || (this.accounts.length > 0 ? this.accounts[0] : null); + const accountName = currentAccount ? (currentAccount.personal ? this.user.profile.name : currentAccount.name) : 'No Account'; + let switchButton = ''; + let accountContainer = ''; + if (this.accounts.length > 1) { - switchButton = ``; - - const accountList = this.accounts.map(acc => ` + switchButton = ` + `; + + accountContainer = ` +
+ ${accountName} + ${switchButton} +
+ `; + + const accountList = this.accounts + .map( + (acc) => ` - `).join(''); + `, + ) + .join(''); - accountSwitcher = ` + accountSwitcher = `
@@ -152,19 +174,28 @@ class PowerStrip extends HTMLElement { `; } + const adminLink = this.user.is_admin + ? `Admin` + : ''; + + const impersonationLink = this.user.is_impersonated + ? `` + : ''; + content = `