diff --git a/.github/workflows/web-deploy-accounts.yml b/.github/workflows/web-deploy-accounts.yml index 8164aea44..61411cac6 100644 --- a/.github/workflows/web-deploy-accounts.yml +++ b/.github/workflows/web-deploy-accounts.yml @@ -24,7 +24,7 @@ jobs: with: node-version: 20 cache: "yarn" - cache-dependency-path: "docs/yarn.lock" + cache-dependency-path: "web/yarn.lock" - name: Install dependencies run: yarn install diff --git a/.github/workflows/web-deploy-auth.yml b/.github/workflows/web-deploy-auth.yml index 63a56b95b..d195b62f8 100644 --- a/.github/workflows/web-deploy-auth.yml +++ b/.github/workflows/web-deploy-auth.yml @@ -24,7 +24,7 @@ jobs: with: node-version: 20 cache: "yarn" - cache-dependency-path: "docs/yarn.lock" + cache-dependency-path: "web/yarn.lock" - name: Install dependencies run: yarn install diff --git a/.github/workflows/web-deploy-cast.yml b/.github/workflows/web-deploy-cast.yml index be4861c71..c5bbca954 100644 --- a/.github/workflows/web-deploy-cast.yml +++ b/.github/workflows/web-deploy-cast.yml @@ -24,7 +24,7 @@ jobs: with: node-version: 20 cache: "yarn" - cache-dependency-path: "docs/yarn.lock" + cache-dependency-path: "web/yarn.lock" - name: Install dependencies run: yarn install diff --git a/.github/workflows/web-deploy-payments.yml b/.github/workflows/web-deploy-payments.yml index 8f4aeae85..367e1db18 100644 --- a/.github/workflows/web-deploy-payments.yml +++ b/.github/workflows/web-deploy-payments.yml @@ -24,7 +24,7 @@ jobs: with: node-version: 20 cache: "yarn" - cache-dependency-path: "docs/yarn.lock" + cache-dependency-path: "web/yarn.lock" - name: Install dependencies run: yarn install diff --git a/.github/workflows/web-deploy-photos.yml b/.github/workflows/web-deploy-photos.yml index 64a88421d..cb3a9db86 100644 --- a/.github/workflows/web-deploy-photos.yml +++ b/.github/workflows/web-deploy-photos.yml @@ -24,7 +24,7 @@ jobs: with: node-version: 20 cache: "yarn" - cache-dependency-path: "docs/yarn.lock" + cache-dependency-path: "web/yarn.lock" - name: Install dependencies run: yarn install diff --git a/.github/workflows/web-deploy-staff.yml b/.github/workflows/web-deploy-staff.yml new file mode 100644 index 000000000..4d386344d --- /dev/null +++ b/.github/workflows/web-deploy-staff.yml @@ -0,0 +1,48 @@ +name: "Deploy (staff)" + +on: + # Run on every push to main that changes web/apps/staff/ + push: + branches: [main] + paths: + - "web/apps/staff/**" + - ".github/workflows/web-deploy-staff.yml" + # Also allow manually running the workflow + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-latest + + defaults: + run: + working-directory: web + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup node and enable yarn caching + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: "yarn" + cache-dependency-path: "web/yarn.lock" + + - name: Install dependencies + run: yarn install + + - name: Build staff + run: yarn build:staff + + - name: Publish staff + uses: cloudflare/pages-action@1 + with: + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + projectName: ente + branch: deploy/staff + directory: web/apps/staff/dist + wranglerVersion: "3" diff --git a/.github/workflows/web-nightly.yml b/.github/workflows/web-nightly.yml index 89d5ecaa5..b5d8c411f 100644 --- a/.github/workflows/web-nightly.yml +++ b/.github/workflows/web-nightly.yml @@ -34,7 +34,7 @@ jobs: with: node-version: 20 cache: "yarn" - cache-dependency-path: "docs/yarn.lock" + cache-dependency-path: "web/yarn.lock" - name: Install dependencies run: yarn install diff --git a/.github/workflows/web-preview.yml b/.github/workflows/web-preview.yml index 2ad73b7a1..8f39c0247 100644 --- a/.github/workflows/web-preview.yml +++ b/.github/workflows/web-preview.yml @@ -34,7 +34,7 @@ jobs: with: node-version: 20 cache: "yarn" - cache-dependency-path: "docs/yarn.lock" + cache-dependency-path: "web/yarn.lock" - name: Install dependencies run: yarn install diff --git a/web/apps/payments/README.md b/web/apps/payments/README.md index eeac0a397..959bedabe 100644 --- a/web/apps/payments/README.md +++ b/web/apps/payments/README.md @@ -1,3 +1,5 @@ +# Payments + Code that runs on `payments.ente.io`. It brokers between our services and Stripe's API for payments. diff --git a/web/apps/staff/.eslintrc.cjs b/web/apps/staff/.eslintrc.cjs new file mode 100644 index 000000000..99b4b9226 --- /dev/null +++ b/web/apps/staff/.eslintrc.cjs @@ -0,0 +1,3 @@ +module.exports = { + extends: ["@/build-config/eslintrc-vite"], +}; diff --git a/web/apps/staff/README.md b/web/apps/staff/README.md new file mode 100644 index 000000000..e54b674d3 --- /dev/null +++ b/web/apps/staff/README.md @@ -0,0 +1,8 @@ +## Staff dashboard + +Web app for staff members to help with support etc. + +### Deployment + +The app gets redeployed whenever a PR is merged into main. See +[docs/deploy.md](../../docs/deploy.md) for more details. diff --git a/web/apps/staff/index.html b/web/apps/staff/index.html new file mode 100644 index 000000000..5600e3a53 --- /dev/null +++ b/web/apps/staff/index.html @@ -0,0 +1,12 @@ + + + + + + Staff | ente.io + + +
+ + + diff --git a/web/apps/staff/package.json b/web/apps/staff/package.json new file mode 100644 index 000000000..2c7c01998 --- /dev/null +++ b/web/apps/staff/package.json @@ -0,0 +1,22 @@ +{ + "name": "staff", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "build": "tsc && vite build", + "dev": "vite", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18", + "react-dom": "^18" + }, + "devDependencies": { + "@/build-config": "*", + "@types/react": "^18", + "@types/react-dom": "^18", + "@vitejs/plugin-react": "^4.2", + "vite": "^5.2" + } +} diff --git a/web/apps/staff/src/App.tsx b/web/apps/staff/src/App.tsx new file mode 100644 index 000000000..f3f0a9f15 --- /dev/null +++ b/web/apps/staff/src/App.tsx @@ -0,0 +1,11 @@ +import React from "react"; +import S from "./utils/strings"; + +export const App: React.FC = () => { + return ( +
+

{S.hello}

+ help.ente.io +
+ ); +}; diff --git a/web/apps/staff/src/components/Container.tsx b/web/apps/staff/src/components/Container.tsx new file mode 100644 index 000000000..bf8c57b5d --- /dev/null +++ b/web/apps/staff/src/components/Container.tsx @@ -0,0 +1,5 @@ +import React from "react"; + +export const Container: React.FC = ({ children }) => ( +
{children}
+); diff --git a/web/apps/staff/src/main.tsx b/web/apps/staff/src/main.tsx new file mode 100644 index 000000000..4ed8c3205 --- /dev/null +++ b/web/apps/staff/src/main.tsx @@ -0,0 +1,13 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import { App } from "./App"; +import "./styles/globals.css"; + +const root = document.getElementById("root"); +if (!root) throw new Error("Could not load root element to render onto"); + +ReactDOM.createRoot(root).render( + + + , +); diff --git a/web/apps/staff/src/services/support-service.ts b/web/apps/staff/src/services/support-service.ts new file mode 100644 index 000000000..4898e1ee5 --- /dev/null +++ b/web/apps/staff/src/services/support-service.ts @@ -0,0 +1,17 @@ +const apiOrigin = import.meta.env.VITE_ENTE_ENDPOINT ?? "https://api.ente.io"; + +/** Fetch details of the user associated with the given {@link authToken}. */ +export const getUserDetails = async (authToken: string) => { + const url = `${apiOrigin}/users/details/v2`; + const res = await fetch(url, { + headers: { + "X-Auth-Token": authToken, + }, + }); + if (!res.ok) throw new Error(`Failed to fetch ${url}: HTTP ${res.status}`); + const json: unknown = await res.json(); + if (json && typeof json === "object") { + return json; + } + throw new Error(`Unexpected response for ${url}: ${JSON.stringify(json)}`); +}; diff --git a/web/apps/staff/src/styles/globals.css b/web/apps/staff/src/styles/globals.css new file mode 100644 index 000000000..158d0e10c --- /dev/null +++ b/web/apps/staff/src/styles/globals.css @@ -0,0 +1,38 @@ +:root { + color-scheme: light dark; + color: #213547; + background-color: #ffffff; +} + +body { + font-family: system-ui, sans-serif; + + margin: 0; + display: flex; + justify-content: center; + text-align: center; + min-height: 100svh; +} + +a { + color: green; +} + +a:hover { + color: darkgreen; +} + +@media (prefers-color-scheme: dark) { + :root { + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + } + + a { + color: lightgreen; + } + + a:hover { + color: chartreuse; + } +} diff --git a/web/apps/staff/src/utils/strings.ts b/web/apps/staff/src/utils/strings.ts new file mode 100644 index 000000000..39b22fcd7 --- /dev/null +++ b/web/apps/staff/src/utils/strings.ts @@ -0,0 +1,13 @@ +/** + * User facing strings in the app. + * + * By keeping them separate, we make our lives easier if/when we need to + * localize the corresponding pages. Right now, these are just the values in the + * default language, English. + */ +const S = { + hello: "Hello Ente!", + error_generic: "Oops, something went wrong.", +}; + +export default S; diff --git a/web/apps/staff/src/vite-env.d.ts b/web/apps/staff/src/vite-env.d.ts new file mode 100644 index 000000000..b49bd06d0 --- /dev/null +++ b/web/apps/staff/src/vite-env.d.ts @@ -0,0 +1,18 @@ +/* Type shims provided by vite, e.g. for asset imports + https://vitejs.dev/guide/features.html#client-types */ + +/// + +/** Types for the vite injected environment variables */ +interface ImportMetaEnv { + /** + * Override the origin (scheme://host:port) of Ente's API to connect to. + * + * This is useful when testing or connecting to alternative installations. + */ + readonly VITE_ENTE_ENDPOINT: string | undefined; +} + +interface ImportMeta { + env: ImportMetaEnv; +} diff --git a/web/apps/staff/tsconfig.json b/web/apps/staff/tsconfig.json new file mode 100644 index 000000000..291fed6ca --- /dev/null +++ b/web/apps/staff/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "@/build-config/tsconfig-vite.json", + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/web/apps/staff/tsconfig.node.json b/web/apps/staff/tsconfig.node.json new file mode 100644 index 000000000..a8d6e3fc8 --- /dev/null +++ b/web/apps/staff/tsconfig.node.json @@ -0,0 +1,4 @@ +{ + "extends": "@/build-config/tsconfig-vite.node.json", + "include": ["vite.config.ts"] +} diff --git a/web/apps/staff/vite.config.ts b/web/apps/staff/vite.config.ts new file mode 100644 index 000000000..d89c4f445 --- /dev/null +++ b/web/apps/staff/vite.config.ts @@ -0,0 +1,7 @@ +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}); diff --git a/web/docs/deploy.md b/web/docs/deploy.md index 6b51a0199..6358cb87f 100644 --- a/web/docs/deploy.md +++ b/web/docs/deploy.md @@ -29,21 +29,22 @@ and publish to [web.ente.io](https://web.ente.io). Here is a list of all the deployments, whether or not they are production deployments, and the action that triggers them: -| URL | Type | Deployment action | -| -------------------------------------------- | ---------- | ------------------------------------------- | -| [web.ente.io](https://web.ente.io) | Production | Push to `deploy/photos` | -| [photos.ente.io](https://photos.ente.io) | Production | Alias of [web.ente.io](https://web.ente.io) | -| [auth.ente.io](https://auth.ente.io) | Production | Push to `deploy/auth` | -| [accounts.ente.io](https://accounts.ente.io) | Production | Push to `deploy/accounts` | -| [cast.ente.io](https://cast.ente.io) | Production | Push to `deploy/cast` | -| [payments.ente.io](https://payments.ente.io) | Production | Push to `deploy/payments` | -| [help.ente.io](https://help.ente.io) | Production | Push to `main` + changes in `docs/` | -| [accounts.ente.sh](https://accounts.ente.sh) | Preview | Nightly deploy of `main` | -| [auth.ente.sh](https://auth.ente.sh) | Preview | Nightly deploy of `main` | -| [cast.ente.sh](https://cast.ente.sh) | Preview | Nightly deploy of `main` | -| [payments.ente.sh](https://payments.ente.sh) | Preview | Nightly deploy of `main` | -| [photos.ente.sh](https://photos.ente.sh) | Preview | Nightly deploy of `main` | -| [preview.ente.sh](https://preview.ente.sh) | Preview | Manually triggered | +| URL | Type | Deployment action | +| -------------------------------------------- | ---------- | -------------------------------------------- | +| [web.ente.io](https://web.ente.io) | Production | Push to `deploy/photos` | +| [photos.ente.io](https://photos.ente.io) | Production | Alias of [web.ente.io](https://web.ente.io) | +| [auth.ente.io](https://auth.ente.io) | Production | Push to `deploy/auth` | +| [accounts.ente.io](https://accounts.ente.io) | Production | Push to `deploy/accounts` | +| [cast.ente.io](https://cast.ente.io) | Production | Push to `deploy/cast` | +| [payments.ente.io](https://payments.ente.io) | Production | Push to `deploy/payments` | +| [help.ente.io](https://help.ente.io) | Production | Push to `main` + changes in `docs/` | +| [staff.ente.sh](https://staff.ente.sh) | Production | Push to `main` + changes in `web/apps/staff` | +| [accounts.ente.sh](https://accounts.ente.sh) | Preview | Nightly deploy of `main` | +| [auth.ente.sh](https://auth.ente.sh) | Preview | Nightly deploy of `main` | +| [cast.ente.sh](https://cast.ente.sh) | Preview | Nightly deploy of `main` | +| [payments.ente.sh](https://payments.ente.sh) | Preview | Nightly deploy of `main` | +| [photos.ente.sh](https://photos.ente.sh) | Preview | Nightly deploy of `main` | +| [preview.ente.sh](https://preview.ente.sh) | Preview | Manually triggered | ### Other subdomains @@ -54,8 +55,8 @@ Apart from this, there are also some other deployments: `albums.ente.io`, it redirects to the `/shared-albums` page (Enhancement: serve it as a separate app with a smaller bundle size). -- `family.ente.io` is currently in a separate repositories (Enhancement: bring - them in here). +- `family.ente.io` is currently in a separate repository (Enhancement: bring + it in here). ### Preview deployments diff --git a/web/package.json b/web/package.json index d27f9a6cb..3b8697bd8 100644 --- a/web/package.json +++ b/web/package.json @@ -13,6 +13,7 @@ "build:cast": "yarn workspace cast next build", "build:payments": "yarn workspace payments build", "build:photos": "yarn workspace photos next build", + "build:staff": "yarn workspace staff build", "deploy:accounts": "open 'https://github.com/ente-io/ente/compare/deploy/accounts...main?quick_pull=1&title=[web]+Deploy+accounts&body=Deploy+accounts.ente.io'", "deploy:auth": "open 'https://github.com/ente-io/ente/compare/deploy/auth...main?quick_pull=1&title=[web]+Deploy+auth&body=Deploy+auth.ente.io'", "deploy:cast": "open 'https://github.com/ente-io/ente/compare/deploy/cast...main?quick_pull=1&title=[web]+Deploy+cast&body=Deploy+cast.ente.io'", @@ -25,6 +26,7 @@ "dev:cast": "yarn workspace cast next dev -p 3001", "dev:payments": "yarn workspace payments dev", "dev:photos": "yarn workspace photos next dev", + "dev:staff": "yarn workspace staff dev", "lint": "yarn prettier --check . && yarn workspaces run eslint --report-unused-disable-directives", "lint-fix": "yarn prettier --write . && yarn workspaces run eslint --fix .", "preview": "yarn preview:photos", @@ -32,7 +34,8 @@ "preview:auth": "yarn build:auth && python3 -m http.server -d apps/auth/out 3000", "preview:cast": "yarn build:cast && python3 -m http.server -d apps/accounts/out 3001", "preview:payments": "yarn workspace payments preview", - "preview:photos": "yarn build:photos && python3 -m http.server -d apps/photos/out 3000" + "preview:photos": "yarn build:photos && python3 -m http.server -d apps/photos/out 3000", + "preview:staff": "yarn workspace staff preview" }, "resolutions": { "libsodium": "0.7.9" diff --git a/web/packages/build-config/eslintrc-vite.js b/web/packages/build-config/eslintrc-vite.js index 131d8902d..37032546b 100644 --- a/web/packages/build-config/eslintrc-vite.js +++ b/web/packages/build-config/eslintrc-vite.js @@ -1,5 +1,5 @@ /* eslint-env node */ module.exports = { - extends: ["./eslintrc-react.js"], + extends: ["./eslintrc-react.js", "plugin:react/jsx-runtime"], ignorePatterns: [".eslintrc.cjs", "vite.config.ts", "dist"], };