divi 0.0.1.dev11__tar.gz → 0.0.1.dev12__tar.gz

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.
Files changed (135) hide show
  1. divi-0.0.1.dev12/.air.toml +61 -0
  2. divi-0.0.1.dev12/.dockerignore +1 -0
  3. divi-0.0.1.dev12/.github/dependabot.yml +11 -0
  4. divi-0.0.1.dev12/.gitignore +51 -0
  5. {divi-0.0.1.dev11 → divi-0.0.1.dev12}/PKG-INFO +3 -1
  6. {divi-0.0.1.dev11 → divi-0.0.1.dev12}/README.md +7 -0
  7. divi-0.0.1.dev12/apps/README.md +15 -0
  8. divi-0.0.1.dev12/apps/graphql/Dockerfile +43 -0
  9. divi-0.0.1.dev12/apps/graphql/README.md +3 -0
  10. divi-0.0.1.dev12/apps/graphql/codegen.ts +19 -0
  11. divi-0.0.1.dev12/apps/graphql/package.json +29 -0
  12. divi-0.0.1.dev12/apps/graphql/src/datasources/auth-api.ts +65 -0
  13. divi-0.0.1.dev12/apps/graphql/src/index.ts +32 -0
  14. divi-0.0.1.dev12/apps/graphql/src/resolvers.ts +82 -0
  15. divi-0.0.1.dev12/apps/graphql/src/schema.graphql +91 -0
  16. divi-0.0.1.dev12/apps/graphql/src/types/api-key.d.ts +4 -0
  17. divi-0.0.1.dev12/apps/graphql/src/types/context.d.ts +7 -0
  18. divi-0.0.1.dev12/apps/graphql/src/types/response.d.ts +16 -0
  19. divi-0.0.1.dev12/apps/graphql/src/types/types.d.ts +374 -0
  20. divi-0.0.1.dev12/apps/graphql/src/types/user.d.ts +9 -0
  21. divi-0.0.1.dev12/apps/graphql/tsconfig.json +13 -0
  22. divi-0.0.1.dev12/apps/web/.gitignore +36 -0
  23. divi-0.0.1.dev12/apps/web/README.md +36 -0
  24. divi-0.0.1.dev12/apps/web/app/favicon.ico +0 -0
  25. divi-0.0.1.dev12/apps/web/app/layout.tsx +27 -0
  26. divi-0.0.1.dev12/apps/web/app/login/page.tsx +43 -0
  27. divi-0.0.1.dev12/apps/web/app/page.tsx +10 -0
  28. divi-0.0.1.dev12/apps/web/app/signup/page.tsx +62 -0
  29. divi-0.0.1.dev12/apps/web/components/ApolloWrapper.tsx +40 -0
  30. divi-0.0.1.dev12/apps/web/components/login-form.tsx +71 -0
  31. divi-0.0.1.dev12/apps/web/components/signup-form.tsx +51 -0
  32. divi-0.0.1.dev12/apps/web/components.json +20 -0
  33. divi-0.0.1.dev12/apps/web/hooks/apolloClient.ts +19 -0
  34. divi-0.0.1.dev12/apps/web/next-env.d.ts +5 -0
  35. divi-0.0.1.dev12/apps/web/next.config.ts +5 -0
  36. divi-0.0.1.dev12/apps/web/package.json +32 -0
  37. divi-0.0.1.dev12/apps/web/postcss.config.mjs +1 -0
  38. divi-0.0.1.dev12/apps/web/public/peeking-angel.png +0 -0
  39. divi-0.0.1.dev12/apps/web/public/thinking-angel.png +0 -0
  40. divi-0.0.1.dev12/apps/web/tsconfig.json +17 -0
  41. divi-0.0.1.dev12/biome.json +30 -0
  42. divi-0.0.1.dev12/divi/__init__.py +16 -0
  43. divi-0.0.1.dev12/divi/decorators/__init__.py +4 -0
  44. divi-0.0.1.dev12/divi/decorators/obs_openai.py +33 -0
  45. divi-0.0.1.dev12/divi/decorators/observable.py +40 -0
  46. divi-0.0.1.dev12/divi/proto/common/v1/common.proto +63 -0
  47. divi-0.0.1.dev12/divi/proto/core/health/v1/health_service.proto +17 -0
  48. divi-0.0.1.dev12/divi/proto/resource/v1/resource.proto +19 -0
  49. divi-0.0.1.dev12/divi/proto/trace/v1/trace.proto +18 -0
  50. divi-0.0.1.dev12/divi/run/__init__.py +3 -0
  51. divi-0.0.1.dev12/divi/run/run.py +2 -0
  52. divi-0.0.1.dev12/divi/services/__init__.py +7 -0
  53. divi-0.0.1.dev12/divi/services/auth/__init__.py +4 -0
  54. divi-0.0.1.dev12/divi/services/auth/auth.py +21 -0
  55. divi-0.0.1.dev12/divi/services/auth/init.py +24 -0
  56. divi-0.0.1.dev12/divi/services/auth/tokman.py +42 -0
  57. divi-0.0.1.dev12/divi/services/core/__init__.py +5 -0
  58. divi-0.0.1.dev12/divi/services/core/core.py +35 -0
  59. {divi-0.0.1.dev11/divi → divi-0.0.1.dev12/divi/services}/core/finish.py +4 -4
  60. {divi-0.0.1.dev11/divi → divi-0.0.1.dev12/divi/services}/core/init.py +23 -22
  61. divi-0.0.1.dev12/divi/services/datapark/__init__.py +3 -0
  62. divi-0.0.1.dev12/divi/services/datapark/datapark.py +6 -0
  63. divi-0.0.1.dev12/divi/services/datapark/init.py +9 -0
  64. divi-0.0.1.dev12/divi/services/finish.py +5 -0
  65. divi-0.0.1.dev12/divi/services/init.py +7 -0
  66. divi-0.0.1.dev12/divi/services/service.py +11 -0
  67. divi-0.0.1.dev12/divi/signals/__init__.py +4 -0
  68. divi-0.0.1.dev12/divi/signals/metric/__init__.py +3 -0
  69. divi-0.0.1.dev12/divi/signals/metric/metric.py +2 -0
  70. divi-0.0.1.dev12/divi/signals/trace/__init__.py +3 -0
  71. divi-0.0.1.dev12/divi/signals/trace/trace.py +2 -0
  72. {divi-0.0.1.dev11 → divi-0.0.1.dev12}/divi/utils.py +8 -0
  73. divi-0.0.1.dev12/docker-compose.yml +62 -0
  74. divi-0.0.1.dev12/go.work.sum +46 -0
  75. divi-0.0.1.dev12/package.json +23 -0
  76. divi-0.0.1.dev12/packages/README.md +15 -0
  77. divi-0.0.1.dev12/pnpm-lock.yaml +6927 -0
  78. divi-0.0.1.dev12/pnpm-workspace.yaml +4 -0
  79. {divi-0.0.1.dev11 → divi-0.0.1.dev12}/pyproject.toml +13 -2
  80. divi-0.0.1.dev12/scripts/key_build.py +46 -0
  81. divi-0.0.1.dev12/services/README.md +15 -0
  82. divi-0.0.1.dev12/services/go.mod +45 -0
  83. divi-0.0.1.dev12/services/go.sum +109 -0
  84. divi-0.0.1.dev12/services/internal/app/auth/handler/api.go +8 -0
  85. divi-0.0.1.dev12/services/internal/app/auth/handler/api_key.go +78 -0
  86. divi-0.0.1.dev12/services/internal/app/auth/handler/auth.go +155 -0
  87. divi-0.0.1.dev12/services/internal/app/auth/handler/user.go +162 -0
  88. divi-0.0.1.dev12/services/internal/app/auth/middleware/auth.go +30 -0
  89. divi-0.0.1.dev12/services/internal/app/auth/router/router.go +37 -0
  90. divi-0.0.1.dev12/services/internal/pkg/auth/auth.go +32 -0
  91. divi-0.0.1.dev12/services/internal/pkg/config/config.go +22 -0
  92. divi-0.0.1.dev12/services/internal/pkg/database/connect.go +39 -0
  93. divi-0.0.1.dev12/services/internal/pkg/database/database.go +6 -0
  94. divi-0.0.1.dev12/services/internal/pkg/model/api_key.go +16 -0
  95. divi-0.0.1.dev12/services/internal/pkg/model/user.go +22 -0
  96. divi-0.0.1.dev12/tests/__init__.py +1 -0
  97. divi-0.0.1.dev12/tests/unit_tests/decorators/test_obs_openai.py +54 -0
  98. divi-0.0.1.dev12/tests/unit_tests/decorators/test_observable.py +18 -0
  99. divi-0.0.1.dev12/turbo.json +35 -0
  100. divi-0.0.1.dev12/uv.lock +637 -0
  101. divi-0.0.1.dev11/.gitignore +0 -16
  102. divi-0.0.1.dev11/apps/README.md +0 -3
  103. divi-0.0.1.dev11/divi/__init__.py +0 -9
  104. divi-0.0.1.dev11/divi/core/__init__.py +0 -5
  105. divi-0.0.1.dev11/divi/core/run.py +0 -34
  106. divi-0.0.1.dev11/divi/proto/core.proto +0 -12
  107. divi-0.0.1.dev11/divi/proto/core_pb2.py +0 -38
  108. divi-0.0.1.dev11/divi/proto/core_pb2.pyi +0 -5
  109. divi-0.0.1.dev11/divi/proto/core_pb2_grpc.py +0 -100
  110. divi-0.0.1.dev11/divi/proto/health.proto +0 -12
  111. divi-0.0.1.dev11/divi/proto/health_pb2.py +0 -39
  112. divi-0.0.1.dev11/divi/proto/health_pb2.pyi +0 -19
  113. divi-0.0.1.dev11/go.work.sum +0 -19
  114. divi-0.0.1.dev11/services/README.md +0 -7
  115. divi-0.0.1.dev11/services/cmd/auth/auth.go +0 -1
  116. divi-0.0.1.dev11/services/cmd/core/core.go +0 -44
  117. divi-0.0.1.dev11/services/cmd/datapark/datapark.go +0 -1
  118. divi-0.0.1.dev11/services/go.mod +0 -15
  119. divi-0.0.1.dev11/services/go.sum +0 -32
  120. divi-0.0.1.dev11/services/internal/pkg/.keep +0 -0
  121. divi-0.0.1.dev11/uv.lock +0 -178
  122. {divi-0.0.1.dev11 → divi-0.0.1.dev12}/LICENSE +0 -0
  123. /divi-0.0.1.dev11/services/internal/app/auth/.keep → /divi-0.0.1.dev12/divi/config/config.py +0 -0
  124. {divi-0.0.1.dev11 → divi-0.0.1.dev12}/docs/README.md +0 -0
  125. {divi-0.0.1.dev11 → divi-0.0.1.dev12}/docs/package_readme.md +0 -0
  126. {divi-0.0.1.dev11 → divi-0.0.1.dev12}/go.work +0 -0
  127. {divi-0.0.1.dev11 → divi-0.0.1.dev12}/hatch.toml +0 -0
  128. {divi-0.0.1.dev11 → divi-0.0.1.dev12}/scripts/README.md +0 -0
  129. {divi-0.0.1.dev11 → divi-0.0.1.dev12}/scripts/hatch_build.py +0 -0
  130. {divi-0.0.1.dev11 → divi-0.0.1.dev12}/scripts/proto_build.py +0 -0
  131. {divi-0.0.1.dev11 → divi-0.0.1.dev12}/services/internal/app/core/.keep +0 -0
  132. {divi-0.0.1.dev11 → divi-0.0.1.dev12}/services/internal/app/datapark/.keep +0 -0
  133. {divi-0.0.1.dev11 → divi-0.0.1.dev12}/services/proto/core.pb.go +0 -0
  134. {divi-0.0.1.dev11 → divi-0.0.1.dev12}/services/proto/core_grpc.pb.go +0 -0
  135. {divi-0.0.1.dev11 → divi-0.0.1.dev12}/services/proto/health.pb.go +0 -0
@@ -0,0 +1,61 @@
1
+ # Config file for [Air](https://github.com/cosmtrek/air) in TOML format
2
+
3
+ # Working directory
4
+ # . or absolute path, please note that the directories following must be under root.
5
+ root = "."
6
+ tmp_dir = "tmp"
7
+
8
+ [build]
9
+ # Just plain old shell command. You could use `make` as well.
10
+ cmd = "go build -o ./tmp/auth ./services/cmd/auth/auth.go"
11
+ # Binary file yields from `cmd`.
12
+ bin = "tmp/auth"
13
+ # Customize binary, can setup environment variables when run your app.
14
+ full_bin = "APP_ENV=dev APP_USER=air ./tmp/auth"
15
+ # Watch these filename extensions.
16
+ include_ext = ["go"]
17
+ # Watch these directories if you specified.
18
+ include_dir = ["services"]
19
+ # Exclude specific regular expressions.
20
+ exclude_regex = ["_test\\.go"]
21
+ # Exclude unchanged files.
22
+ exclude_unchanged = true
23
+ # Follow symlink for directories
24
+ follow_symlink = true
25
+ # This log file places in your tmp_dir.
26
+ log = "air.log"
27
+ # Poll files for changes instead of using fsnotify.
28
+ poll = false
29
+ # Poll interval (defaults to the minimum interval of 500ms).
30
+ poll_interval = 500 # ms
31
+ # It's not necessary to trigger build each time file changes if it's too frequent.
32
+ delay = 0 # ms
33
+ # Stop running old binary when build errors occur.
34
+ stop_on_error = true
35
+ # Send Interrupt signal before killing process (windows does not support this feature)
36
+ send_interrupt = false
37
+ # Delay after sending Interrupt signal
38
+ kill_delay = 500 # ms
39
+ # Rerun binary or not
40
+ rerun = false
41
+ # Delay after each executions
42
+ rerun_delay = 500
43
+ # Add additional arguments when running binary (bin/full_bin). Will run './tmp/main hello world'.
44
+ args_bin = []
45
+
46
+ [log]
47
+ # Show log time
48
+ time = false
49
+ # Only show main log (silences watcher, build, runner)
50
+ main_only = false
51
+
52
+ [color]
53
+ # Customize each part's color. If no color found, use the raw app log.
54
+ main = "magenta"
55
+ watcher = "cyan"
56
+ build = "yellow"
57
+ runner = "green"
58
+
59
+ [misc]
60
+ # Delete tmp directory on exit
61
+ clean_on_exit = true
@@ -0,0 +1 @@
1
+ node_modules
@@ -0,0 +1,11 @@
1
+ # To get started with Dependabot version updates, you'll need to specify which
2
+ # package ecosystems to update and where the package manifests are located.
3
+ # Please see the documentation for all configuration options:
4
+ # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5
+
6
+ version: 2
7
+ updates:
8
+ - package-ecosystem: "" # See documentation for possible values
9
+ directory: "/" # Location of package manifests
10
+ schedule:
11
+ interval: "weekly"
@@ -0,0 +1,51 @@
1
+ .vscode/
2
+ .idea/
3
+
4
+ # Byte-compiled / optimized / DLL files
5
+ __pycache__/
6
+ *.py[cod]
7
+ *$py.class
8
+
9
+ # Distribution / packaging
10
+ .next/
11
+ dist/
12
+ out/
13
+ tmp/
14
+ build/
15
+ bin/
16
+
17
+ # Environments
18
+ .venv/
19
+ .env
20
+
21
+ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
22
+
23
+ # Dependencies
24
+ node_modules
25
+ .pnp
26
+ .pnp.js
27
+
28
+ # Local env files
29
+ .env
30
+ .env.local
31
+ .env.development.local
32
+ .env.test.local
33
+ .env.production.local
34
+
35
+ # Testing
36
+ coverage
37
+
38
+ # Turbo
39
+ .turbo
40
+
41
+ # Vercel
42
+ .vercel
43
+
44
+ # Debug
45
+ npm-debug.log*
46
+ yarn-debug.log*
47
+ yarn-error.log*
48
+
49
+ # Misc
50
+ .DS_Store
51
+ *.pem
@@ -1,11 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: divi
3
- Version: 0.0.1.dev11
3
+ Version: 0.0.1.dev12
4
4
  Summary: The Agent Platform for Observability & Evaluation
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.11
7
7
  Requires-Dist: grpcio>=1.69.0
8
8
  Requires-Dist: protobuf>=5.29.3
9
+ Requires-Dist: pyjwt>=2.10.1
10
+ Requires-Dist: requests>=2.32.3
9
11
  Description-Content-Type: text/markdown
10
12
 
11
13
  # Divine Agent
@@ -2,6 +2,12 @@
2
2
 
3
3
  Agent Platform for Observability • Evaluation • Playground
4
4
 
5
+ ## TODO
6
+
7
+ - [ ] trace: obs_openai()
8
+ - [ ] trace: @obs
9
+ - [ ] datapark services
10
+
5
11
  ## Structure
6
12
 
7
13
  > Divine Agent is a monorepo project. The project is structured as follows:
@@ -21,3 +27,4 @@ Agent Platform for Observability • Evaluation • Playground
21
27
  1. [uv](https://github.com/astral-sh/uv): An extremely fast Python package and project manager, written in Rust.
22
28
  2. [hatch](https://github.com/pypa/hatch): Hatch is a modern, extensible Python project manager.
23
29
  3. [github cli](https://cli.github.com/manual): GitHub CLI, or gh, is a command-line interface to GitHub for use in your terminal or your scripts.
30
+ 4. [turborepo](https://github.com/vercel/turborepo): Build system optimized for JavaScript and TypeScript, written in Rust
@@ -0,0 +1,15 @@
1
+ # Apps
2
+
3
+ > A series of web applications.
4
+
5
+ ## Table of Contents
6
+
7
+ | Name | Description | Port |
8
+ | --- | --- | --- |
9
+ | graphql | A GraphQL server | 4000 |
10
+ | web | A Next.js web server | 4001 |
11
+ | docs | A documentation server | 4002 |
12
+
13
+ ## Rules
14
+
15
+ 1. [Split project files by feature or route](https://nextjs.org/docs/app/getting-started/project-structure#split-project-files-by-feature-or-route)
@@ -0,0 +1,43 @@
1
+ FROM node:23-alpine AS base
2
+
3
+ FROM base AS builder
4
+ # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
5
+ RUN apk update
6
+ RUN apk add --no-cache libc6-compat
7
+ # 启用 corepack 并激活 pnpm
8
+ RUN corepack enable && corepack prepare pnpm@latest --activate
9
+ # Set working directory
10
+ WORKDIR /app
11
+ # 使用 yarn 全局安装 turbo
12
+ RUN yarn global add turbo
13
+ COPY . .
14
+ RUN turbo prune @app/graphql --docker
15
+
16
+ # Add lockfile and package.json's of isolated subworkspace
17
+ FROM base AS installer
18
+ RUN apk update
19
+ RUN apk add --no-cache libc6-compat
20
+ # 启用 corepack 并激活 pnpm
21
+ RUN corepack enable && corepack prepare pnpm@latest --activate
22
+ WORKDIR /app
23
+
24
+ # First install dependencies (as they change less often)
25
+ COPY --from=builder /app/out/json/ .
26
+ COPY --from=builder /app/out/pnpm-lock.yaml .
27
+ RUN pnpm install
28
+
29
+ # Build the project and its dependencies
30
+ COPY --from=builder /app/out/full/ .
31
+ RUN pnpm turbo build
32
+
33
+ FROM base AS runner
34
+ WORKDIR /app
35
+
36
+ # Don't run production as root
37
+ RUN addgroup --system --gid 1001 divi
38
+ RUN adduser --system --uid 1001 divi
39
+ USER divi
40
+ COPY --from=installer /app .
41
+
42
+ EXPOSE 4000
43
+ CMD ["node", "apps/graphql/dist/index.js"]
@@ -0,0 +1,3 @@
1
+ # Catstronauts - server
2
+
3
+ The starting point of the `server` code for Odyssey Lift-off I course.
@@ -0,0 +1,19 @@
1
+ import type { CodegenConfig } from '@graphql-codegen/cli';
2
+
3
+ const config: CodegenConfig = {
4
+ schema: './src/schema.graphql',
5
+ generates: {
6
+ './src/types/types.d.ts': {
7
+ plugins: ['typescript', 'typescript-resolvers'],
8
+ config: {
9
+ contextType: './context#DataSourceContext',
10
+ mappers: {
11
+ User: './user#UserModel',
12
+ APIKey: './api-key#APIKeyModel',
13
+ },
14
+ },
15
+ },
16
+ },
17
+ };
18
+
19
+ export default config;
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@app/graphql",
3
+ "private": true,
4
+ "version": "0.1.0",
5
+ "main": "index.ts",
6
+ "scripts": {
7
+ "dev": "ts-node-dev --respawn ./src/index.ts",
8
+ "build": "tsc",
9
+ "start": "npm run build && nodemon ./dist/index.js",
10
+ "prebuild": "graphql-codegen",
11
+ "postbuild": "cp src/schema.graphql dist/"
12
+ },
13
+ "dependencies": {
14
+ "@apollo/datasource-rest": "^6.4.1",
15
+ "@apollo/server": "^4.11.3",
16
+ "@apollo/utils.keyvaluecache": "^3.1.0",
17
+ "graphql": "^16.10.0",
18
+ "graphql-tag": "^2.12.6"
19
+ },
20
+ "devDependencies": {
21
+ "@graphql-codegen/cli": "^4.0.1",
22
+ "@graphql-codegen/typescript": "^4.1.5",
23
+ "@graphql-codegen/typescript-resolvers": "^4.4.4",
24
+ "@workspace/typescript-config": "workspace:*",
25
+ "nodemon": "^3.1.9",
26
+ "ts-node-dev": "^2.0.0",
27
+ "typescript": "^5.7.3"
28
+ }
29
+ }
@@ -0,0 +1,65 @@
1
+ import type { APIKeyModel } from '@/types/api-key';
2
+ import type { FetchResponse } from '@/types/response';
3
+ import type { UserModel } from '@/types/user';
4
+ import { type AugmentedRequest, RESTDataSource } from '@apollo/datasource-rest';
5
+ import type { KeyValueCache } from '@apollo/utils.keyvaluecache';
6
+
7
+ export class AuthAPI extends RESTDataSource {
8
+ override baseURL = process.env.AUTH_SERVICE_URL ?? 'http://localhost:3000/';
9
+ private token: string;
10
+
11
+ constructor(options: { token: string; cache: KeyValueCache }) {
12
+ super(options);
13
+ this.token = options.token;
14
+ }
15
+
16
+ override willSendRequest(_path: string, request: AugmentedRequest) {
17
+ request.headers.authorization = this.token;
18
+ }
19
+
20
+ async createUser(email: string, password: string, username: string) {
21
+ return await this.post<FetchResponse<UserModel>>('/api/user/', {
22
+ body: { email, password, username },
23
+ });
24
+ }
25
+
26
+ async getUser(userId: string) {
27
+ return await this.get<FetchResponse<UserModel>>(`/api/user/${userId}`);
28
+ }
29
+
30
+ async updateUser(userId: string, name: string) {
31
+ return await this.patch<FetchResponse<UserModel>>(`/api/user/${userId}`, {
32
+ body: { name },
33
+ });
34
+ }
35
+
36
+ async deleteUser(userId: string, password: string) {
37
+ return await this.delete<FetchResponse<null>>(`/api/user/${userId}`, {
38
+ body: { password },
39
+ });
40
+ }
41
+
42
+ async getAPIKeys() {
43
+ return await this.get<FetchResponse<APIKeyModel[]>>('/api/api_key/');
44
+ }
45
+
46
+ async createAPIKey() {
47
+ return await this.post<FetchResponse<APIKeyModel>>('/api/api_key/');
48
+ }
49
+
50
+ async revokeAPIKey(apiKeyId: string) {
51
+ return await this.delete<FetchResponse<null>>(`/api/api_key/${apiKeyId}`);
52
+ }
53
+
54
+ async login(identity: string, password: string) {
55
+ return await this.post<FetchResponse<string>>('/api/auth/login', {
56
+ body: { identity, password },
57
+ });
58
+ }
59
+
60
+ async loginWithAPIKey(apiKey: string) {
61
+ return await this.post<FetchResponse<string>>('/api/auth/api_key', {
62
+ body: { api_key: apiKey },
63
+ });
64
+ }
65
+ }
@@ -0,0 +1,32 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { ApolloServer } from '@apollo/server';
4
+ import { startStandaloneServer } from '@apollo/server/standalone';
5
+ import gql from 'graphql-tag';
6
+ import { AuthAPI } from './datasources/auth-api';
7
+ import { resolvers } from './resolvers';
8
+
9
+ const typeDefs = gql(
10
+ readFileSync(join(__dirname, './schema.graphql'), { encoding: 'utf-8' })
11
+ );
12
+
13
+ async function startApolloServer() {
14
+ const server = new ApolloServer({ typeDefs, resolvers });
15
+ const { url } = await startStandaloneServer(server, {
16
+ context: ({ req }) => {
17
+ const token = req.headers.authorization || '';
18
+ const { cache } = server;
19
+ return Promise.resolve({
20
+ dataSources: {
21
+ authAPI: new AuthAPI({ token, cache }),
22
+ },
23
+ });
24
+ },
25
+ });
26
+ console.info(`
27
+ 🚀 Server is running!
28
+ 📭 Query at ${url}
29
+ `);
30
+ }
31
+
32
+ startApolloServer();
@@ -0,0 +1,82 @@
1
+ import type {
2
+ ErrorResponse,
3
+ FetchResponse,
4
+ MutationResponse,
5
+ } from '@/types/response';
6
+ import type { Resolvers } from '@/types/types';
7
+ import type { GraphQLError } from 'graphql';
8
+
9
+ export const resolvers: Resolvers = {
10
+ Query: {
11
+ user: async (_, { id }, { dataSources }) => {
12
+ return (await dataSources.authAPI.getUser(id)).data;
13
+ },
14
+ },
15
+ Mutation: {
16
+ createAPIKey: async (_, _args, { dataSources }) => {
17
+ return await mutationAdaptor(dataSources.authAPI.createAPIKey());
18
+ },
19
+ revokeAPIKey: async (_, { id }, { dataSources }) => {
20
+ return await mutationAdaptor(dataSources.authAPI.revokeAPIKey(id));
21
+ },
22
+ login: async (_, { identity, password }, { dataSources }) => {
23
+ return await mutationAdaptor(
24
+ dataSources.authAPI.login(identity, password)
25
+ );
26
+ },
27
+ loginWithAPIKey: async (_, { api_key }, { dataSources }) => {
28
+ return await mutationAdaptor(
29
+ dataSources.authAPI.loginWithAPIKey(api_key)
30
+ );
31
+ },
32
+ deleteUser: async (_, { id, password }, { dataSources }) => {
33
+ return await mutationAdaptor(
34
+ dataSources.authAPI.deleteUser(id, password)
35
+ );
36
+ },
37
+ updateUser: async (_, { id, name }, { dataSources }) => {
38
+ return await mutationAdaptor(dataSources.authAPI.updateUser(id, name));
39
+ },
40
+ createUser: async (_, { email, password, username }, { dataSources }) => {
41
+ return await mutationAdaptor(
42
+ dataSources.authAPI.createUser(email, password, username)
43
+ );
44
+ },
45
+ },
46
+ User: {
47
+ api_keys: async (_user, _args, { dataSources }) => {
48
+ return (await dataSources.authAPI.getAPIKeys()).data;
49
+ },
50
+ },
51
+ };
52
+
53
+ /**
54
+ * mutationAdaptor is a utility function that adapts a FetchResponse to a MutationResponse
55
+ * @param { Promise<FetchResponse<T>> } f
56
+ * @returns { MutationResponse<T> }
57
+ */
58
+ async function mutationAdaptor<T>(
59
+ f: Promise<FetchResponse<T>>
60
+ ): Promise<MutationResponse<T | null>> {
61
+ return f
62
+ .then((response): MutationResponse<T> => {
63
+ return {
64
+ ...response,
65
+ code: 200,
66
+ success: true,
67
+ };
68
+ })
69
+ .catch((error: GraphQLError): MutationResponse<null> => {
70
+ const response = error.extensions.response as ErrorResponse;
71
+ // message = response.body if response.body is string else response.body.message
72
+ if (typeof response.body === 'string') {
73
+ response.body = { message: response.body, data: null };
74
+ }
75
+ return {
76
+ code: response.status,
77
+ success: false,
78
+ message: response.body.message,
79
+ data: null,
80
+ };
81
+ });
82
+ }
@@ -0,0 +1,91 @@
1
+ "Query is a collection of queries that can be made to the API"
2
+ type Query {
3
+ "Fetch a specific user by id"
4
+ user(id: ID!): User!
5
+ }
6
+
7
+ "User is a registered user of the application"
8
+ type User {
9
+ id: ID!
10
+ name: String
11
+ username: String!
12
+ email: String!
13
+ api_keys: [APIKey]
14
+ }
15
+
16
+ "APIKey is a key used to authenticate requests to the API"
17
+ type APIKey {
18
+ id: ID!
19
+ api_key: String!
20
+ }
21
+
22
+ "Mutation is a collection of mutations that can be made to the API"
23
+ type Mutation {
24
+ "Auth Mutations"
25
+ login(identity: String!, password: String!): CreateTokenResponse!
26
+ loginWithAPIKey(api_key: String!): CreateTokenResponse!
27
+ "API Key Mutations"
28
+ createAPIKey: CreateAPIKeyResponse!
29
+ revokeAPIKey(id: ID!): RevokeAPIKeyResponse!
30
+ "User Mutations"
31
+ createUser(
32
+ username: String!
33
+ email: String!
34
+ password: String!
35
+ ): CreateUserResponse!
36
+ updateUser(id: ID!, name: String!): UpdateUserResponse!
37
+ deleteUser(id: ID!, password: String!): DeleteUserResponse!
38
+ }
39
+
40
+ "MutationResponse is a response to a mutation"
41
+ interface MutationResponse {
42
+ code: Int!
43
+ message: String!
44
+ success: Boolean!
45
+ }
46
+
47
+ "CreateAPIKeyResponse is a response to the createAPIKey mutation"
48
+ type CreateAPIKeyResponse implements MutationResponse {
49
+ code: Int!
50
+ message: String!
51
+ success: Boolean!
52
+ data: APIKey
53
+ }
54
+
55
+ "RevokeAPIKeyResponse is a response to the revokeAPIKey mutation"
56
+ type RevokeAPIKeyResponse implements MutationResponse {
57
+ code: Int!
58
+ message: String!
59
+ success: Boolean!
60
+ }
61
+
62
+ "CreateTokenResponse is a response to the login mutation"
63
+ type CreateTokenResponse implements MutationResponse {
64
+ code: Int!
65
+ message: String!
66
+ success: Boolean!
67
+ data: String
68
+ }
69
+
70
+ "DeleteUserResponse is a response to the deleteUser mutation"
71
+ type DeleteUserResponse implements MutationResponse {
72
+ code: Int!
73
+ message: String!
74
+ success: Boolean!
75
+ }
76
+
77
+ "UpdateUserResponse is a response to the updateUser mutation"
78
+ type UpdateUserResponse implements MutationResponse {
79
+ code: Int!
80
+ message: String!
81
+ success: Boolean!
82
+ data: User
83
+ }
84
+
85
+ "CreateUserResponse is a response to the createUser mutation"
86
+ type CreateUserResponse implements MutationResponse {
87
+ code: Int!
88
+ message: String!
89
+ success: Boolean!
90
+ data: User
91
+ }
@@ -0,0 +1,4 @@
1
+ export type APIKeyModel = {
2
+ id: number;
3
+ api_key: string;
4
+ };
@@ -0,0 +1,7 @@
1
+ import type { AuthAPI } from "@/datasources/auth-api";
2
+
3
+ export type DataSourceContext = {
4
+ dataSources: {
5
+ authAPI: AuthAPI;
6
+ };
7
+ };
@@ -0,0 +1,16 @@
1
+ export type FetchResponse<T> = {
2
+ message: string;
3
+ data: T;
4
+ };
5
+
6
+ export type MutationResponse<T> = FetchResponse<T> & {
7
+ code: number;
8
+ success: boolean;
9
+ };
10
+
11
+ export type ErrorResponse = {
12
+ url: string;
13
+ status: number;
14
+ statusText: string;
15
+ body: FetchResponse<null>;
16
+ };