@studious-lms/server 1.25.0 → 1.27.0
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.
- package/.env.example +3 -1
- package/.github/workflows/npm-publish.yml +1 -0
- package/AGENTS.md +84 -0
- package/dist/models/cloudStorage.d.ts +11 -0
- package/dist/models/cloudStorage.d.ts.map +1 -1
- package/dist/models/cloudStorage.js +18 -2
- package/dist/models/cloudStorage.js.map +1 -1
- package/dist/models/conversation.d.ts +1 -0
- package/dist/models/conversation.d.ts.map +1 -1
- package/dist/models/labChat.d.ts +40 -0
- package/dist/models/labChat.d.ts.map +1 -1
- package/dist/models/labChat.js +32 -2
- package/dist/models/labChat.js.map +1 -1
- package/dist/models/marketplace.d.ts +35 -0
- package/dist/models/marketplace.d.ts.map +1 -1
- package/dist/models/marketplace.js +9 -2
- package/dist/models/marketplace.js.map +1 -1
- package/dist/models/message.d.ts +3 -0
- package/dist/models/message.d.ts.map +1 -1
- package/dist/models/module.d.ts +228 -0
- package/dist/models/module.d.ts.map +1 -0
- package/dist/models/module.js +69 -0
- package/dist/models/module.js.map +1 -0
- package/dist/pipelines/aiLabChat.d.ts +2 -0
- package/dist/pipelines/aiLabChat.d.ts.map +1 -1
- package/dist/pipelines/aiLabChat.js +60 -16
- package/dist/pipelines/aiLabChat.js.map +1 -1
- package/dist/pipelines/aiLabChatContract.d.ts +170 -2
- package/dist/pipelines/aiLabChatContract.d.ts.map +1 -1
- package/dist/pipelines/aiLabChatContract.js +30 -2
- package/dist/pipelines/aiLabChatContract.js.map +1 -1
- package/dist/pipelines/aiNewtonChat.d.ts.map +1 -1
- package/dist/pipelines/aiNewtonChat.js +30 -5
- package/dist/pipelines/aiNewtonChat.js.map +1 -1
- package/dist/pipelines/gradeWorksheet.d.ts.map +1 -1
- package/dist/pipelines/gradeWorksheet.js +4 -2
- package/dist/pipelines/gradeWorksheet.js.map +1 -1
- package/dist/pipelines/labChatPrompt.d.ts +1 -1
- package/dist/pipelines/labChatPrompt.d.ts.map +1 -1
- package/dist/pipelines/labChatPrompt.js +13 -14
- package/dist/pipelines/labChatPrompt.js.map +1 -1
- package/dist/routers/_app.d.ts +1185 -215
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/_app.js +4 -2
- package/dist/routers/_app.js.map +1 -1
- package/dist/routers/admin.d.ts +0 -1
- package/dist/routers/admin.d.ts.map +1 -1
- package/dist/routers/conversation.d.ts +1 -0
- package/dist/routers/conversation.d.ts.map +1 -1
- package/dist/routers/labChat.d.ts +10 -0
- package/dist/routers/labChat.d.ts.map +1 -1
- package/dist/routers/labChat.js +6 -3
- package/dist/routers/labChat.js.map +1 -1
- package/dist/routers/marketplace.d.ts +41 -0
- package/dist/routers/marketplace.d.ts.map +1 -1
- package/dist/routers/marketplace.js +13 -3
- package/dist/routers/marketplace.js.map +1 -1
- package/dist/routers/message.d.ts +37 -4
- package/dist/routers/message.d.ts.map +1 -1
- package/dist/routers/message.js +24 -7
- package/dist/routers/message.js.map +1 -1
- package/dist/routers/module.d.ts +345 -0
- package/dist/routers/module.d.ts.map +1 -0
- package/dist/routers/module.js +82 -0
- package/dist/routers/module.js.map +1 -0
- package/dist/routers/parent.d.ts +18 -0
- package/dist/routers/parent.d.ts.map +1 -1
- package/dist/routers/parent.js +10 -3
- package/dist/routers/parent.js.map +1 -1
- package/dist/routers/studentProgress.d.ts +44 -0
- package/dist/routers/studentProgress.d.ts.map +1 -1
- package/dist/routers/studentProgress.js +12 -3
- package/dist/routers/studentProgress.js.map +1 -1
- package/dist/routes/aiStream.js +32 -2
- package/dist/routes/aiStream.js.map +1 -1
- package/dist/seedDatabase.d.ts.map +1 -1
- package/dist/seedDatabase.js +107 -3
- package/dist/seedDatabase.js.map +1 -1
- package/dist/services/adminUser.d.ts +0 -1
- package/dist/services/adminUser.d.ts.map +1 -1
- package/dist/services/adminUser.js +7 -4
- package/dist/services/adminUser.js.map +1 -1
- package/dist/services/aiChatStream.d.ts +15 -0
- package/dist/services/aiChatStream.d.ts.map +1 -1
- package/dist/services/aiChatStream.js +144 -74
- package/dist/services/aiChatStream.js.map +1 -1
- package/dist/services/aiGeneration.d.ts +4 -0
- package/dist/services/aiGeneration.d.ts.map +1 -0
- package/dist/services/aiGeneration.js +88 -0
- package/dist/services/aiGeneration.js.map +1 -0
- package/dist/services/aiProposalTools.d.ts +4 -1
- package/dist/services/aiProposalTools.d.ts.map +1 -1
- package/dist/services/aiProposalTools.js +128 -3
- package/dist/services/aiProposalTools.js.map +1 -1
- package/dist/services/aiProposals.d.ts +70 -0
- package/dist/services/aiProposals.d.ts.map +1 -0
- package/dist/services/aiProposals.js +363 -0
- package/dist/services/aiProposals.js.map +1 -0
- package/dist/services/assignment.d.ts.map +1 -1
- package/dist/services/assignment.js +8 -3
- package/dist/services/assignment.js.map +1 -1
- package/dist/services/conversation.d.ts +1 -0
- package/dist/services/conversation.d.ts.map +1 -1
- package/dist/services/labChat.d.ts +10 -0
- package/dist/services/labChat.d.ts.map +1 -1
- package/dist/services/labChat.js +52 -3
- package/dist/services/labChat.js.map +1 -1
- package/dist/services/marketplace.d.ts +36 -0
- package/dist/services/marketplace.d.ts.map +1 -1
- package/dist/services/marketplace.js +30 -3
- package/dist/services/marketplace.js.map +1 -1
- package/dist/services/message.d.ts +24 -8
- package/dist/services/message.d.ts.map +1 -1
- package/dist/services/message.js +170 -117
- package/dist/services/message.js.map +1 -1
- package/dist/services/module.d.ts +288 -0
- package/dist/services/module.d.ts.map +1 -0
- package/dist/services/module.js +518 -0
- package/dist/services/module.js.map +1 -0
- package/dist/services/parentPortal.d.ts +14 -0
- package/dist/services/parentPortal.d.ts.map +1 -1
- package/dist/services/parentPortal.js +72 -2
- package/dist/services/parentPortal.js.map +1 -1
- package/dist/services/school.d.ts.map +1 -1
- package/dist/services/school.js +17 -5
- package/dist/services/school.js.map +1 -1
- package/dist/services/studentProgress.d.ts +50 -0
- package/dist/services/studentProgress.d.ts.map +1 -1
- package/dist/services/studentProgress.js +107 -2
- package/dist/services/studentProgress.js.map +1 -1
- package/dist/utils/inference.d.ts +10 -0
- package/dist/utils/inference.d.ts.map +1 -1
- package/dist/utils/inference.js +77 -2
- package/dist/utils/inference.js.map +1 -1
- package/dist/workers/processors/createRunJobWorker.js +17 -16
- package/dist/workers/processors/createRunJobWorker.js.map +1 -1
- package/package.json +1 -1
- package/prisma/migrations/20260708013000_add_modules/migration.sql +100 -0
- package/prisma/migrations/20260811130000_add_ai_message_feedback/migration.sql +7 -0
- package/prisma/schema.prisma +101 -0
- package/src/models/cloudStorage.ts +19 -0
- package/src/models/labChat.ts +34 -0
- package/src/models/marketplace.ts +16 -0
- package/src/models/module.ts +77 -0
- package/src/pipelines/aiLabChat.ts +79 -18
- package/src/pipelines/aiLabChatContract.ts +35 -0
- package/src/pipelines/aiNewtonChat.ts +34 -2
- package/src/pipelines/gradeWorksheet.ts +6 -0
- package/src/pipelines/labChatPrompt.ts +11 -12
- package/src/routers/_app.ts +2 -0
- package/src/routers/labChat.ts +5 -0
- package/src/routers/marketplace.ts +16 -0
- package/src/routers/message.ts +34 -5
- package/src/routers/module.ts +118 -0
- package/src/routers/parent.ts +11 -0
- package/src/routers/studentProgress.ts +15 -0
- package/src/routes/aiStream.ts +32 -0
- package/src/seedDatabase.ts +107 -2
- package/src/services/adminUser.ts +5 -2
- package/src/services/aiChatStream.ts +83 -7
- package/src/services/aiGeneration.ts +92 -0
- package/src/services/aiProposalTools.ts +166 -1
- package/src/services/aiProposals.ts +428 -0
- package/src/services/assignment.ts +12 -1
- package/src/services/labChat.ts +65 -0
- package/src/services/marketplace.ts +39 -0
- package/src/services/message.ts +205 -142
- package/src/services/module.ts +683 -0
- package/src/services/parentPortal.ts +85 -0
- package/src/services/school.ts +15 -3
- package/src/services/studentProgress.ts +135 -0
- package/src/utils/inference.ts +85 -0
- package/src/workers/processors/createRunJobWorker.ts +17 -17
- package/tests/unit/services/adminUser.test.ts +75 -0
- package/tests/unit/utils/adminPanelAccess.test.ts +26 -0
package/.env.example
CHANGED
|
@@ -20,7 +20,9 @@ PORT=3001
|
|
|
20
20
|
# Environment: development, test, production
|
|
21
21
|
NODE_ENV=development
|
|
22
22
|
|
|
23
|
-
# Admin panel (tRPC `admin.*`):
|
|
23
|
+
# Admin panel (tRPC `admin.*`): authenticated session plus the
|
|
24
|
+
# `x-admin-password` request header. The password is currently a fixed
|
|
25
|
+
# value in `src/utils/adminPanelAccess.ts` (not an env var).
|
|
24
26
|
|
|
25
27
|
# Logging Configuration
|
|
26
28
|
# Options: debug, info, warn, error
|
package/AGENTS.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Operating guide for coding agents in `studious-lms/server` — the Studious
|
|
4
|
+
backend (Express + tRPC + Prisma). This package is also published to npm as
|
|
5
|
+
**`@studious-lms/server`**: the frontend (`studious-lms/studious`) imports its
|
|
6
|
+
tRPC router types (`RouterInputs` / `RouterOutputs`) from the published
|
|
7
|
+
package, **not** from the repo. Any change to a router's input/output shape is
|
|
8
|
+
invisible to the frontend until a new version is published and the frontend
|
|
9
|
+
bumps its dependency.
|
|
10
|
+
|
|
11
|
+
## Non-Negotiables
|
|
12
|
+
|
|
13
|
+
- Base branch for PRs and merges is `staging`. Do not target `main` unless
|
|
14
|
+
explicitly requested by maintainers.
|
|
15
|
+
- Conventional Commits for messages, branch names as
|
|
16
|
+
`[type]/[code-id]-description` (see the studious `AGENTS.md` for the full
|
|
17
|
+
convention — both repos share it).
|
|
18
|
+
- Always request `@coderabbitai` review in the PR body.
|
|
19
|
+
- **If your change alters any tRPC input/output shape, follow the
|
|
20
|
+
[Release npm + sync frontend types](#skill-release-npm--sync-frontend-types)
|
|
21
|
+
skill before the companion frontend PR merges.**
|
|
22
|
+
|
|
23
|
+
## Skill: Release npm + sync frontend types
|
|
24
|
+
|
|
25
|
+
Use when server API/type changes must reach the frontend — i.e. whenever a
|
|
26
|
+
companion `studious-lms/studious` PR consumes new or changed
|
|
27
|
+
`RouterInputs`/`RouterOutputs`, or when asked to "release npm" / "sync types".
|
|
28
|
+
|
|
29
|
+
Publishing is tag-driven: `.github/workflows/npm-publish.yml` runs on any
|
|
30
|
+
pushed `v*` tag (`npm ci` → `npm run build` → `npm publish --access public`).
|
|
31
|
+
There is no publish from merges; **the tag is the release.**
|
|
32
|
+
|
|
33
|
+
1. **Pick the version.** Check what is already published
|
|
34
|
+
(`npm view @studious-lms/server version`) and what has landed since that
|
|
35
|
+
tag (`git log v<current>..HEAD --oneline`). Any `feat` since the last
|
|
36
|
+
release ⇒ minor bump; fixes only ⇒ patch. Breaking router changes ⇒ major
|
|
37
|
+
(rare — coordinate first).
|
|
38
|
+
2. **Bump on the branch being released** (usually the feature branch about to
|
|
39
|
+
merge, or `staging`):
|
|
40
|
+
```bash
|
|
41
|
+
npm version <x.y.z> --no-git-tag-version
|
|
42
|
+
git add package.json package-lock.json
|
|
43
|
+
git commit -m "chore(release): bump version to <x.y.z>"
|
|
44
|
+
git push -u origin <branch>
|
|
45
|
+
```
|
|
46
|
+
3. **Tag that commit and push the tag** — this triggers the publish:
|
|
47
|
+
```bash
|
|
48
|
+
git tag v<x.y.z>
|
|
49
|
+
git push origin v<x.y.z>
|
|
50
|
+
```
|
|
51
|
+
4. **Verify the publish** before touching the frontend:
|
|
52
|
+
```bash
|
|
53
|
+
gh run list --workflow npm-publish.yml --limit 1 # wait for success
|
|
54
|
+
npm view @studious-lms/server version # must print <x.y.z>
|
|
55
|
+
```
|
|
56
|
+
If the workflow fails, fix and re-release as a new patch version — never
|
|
57
|
+
move or force-push a tag that may already have published.
|
|
58
|
+
5. **Sync the frontend.** In `studious-lms/studious`, on the companion branch:
|
|
59
|
+
```bash
|
|
60
|
+
npm install @studious-lms/server@^<x.y.z>
|
|
61
|
+
npx tsc --noEmit # types must compile against the new package
|
|
62
|
+
git add package.json package-lock.json
|
|
63
|
+
git commit -m "chore(deps): bump @studious-lms/server to <x.y.z>"
|
|
64
|
+
git push
|
|
65
|
+
```
|
|
66
|
+
6. **Merge order:** server PR → (release already published) → frontend PR.
|
|
67
|
+
The frontend PR must not merge while it still pins a version that lacks
|
|
68
|
+
the server changes it consumes.
|
|
69
|
+
|
|
70
|
+
Notes:
|
|
71
|
+
|
|
72
|
+
- The tagged commit should contain everything you intend to ship — the
|
|
73
|
+
package publishes from the tag's tree, not from `staging`/`main`.
|
|
74
|
+
- `npm run build` runs `tsc` plus Sentry sourcemap upload; the workflow
|
|
75
|
+
provides `SENTRY_AUTH_TOKEN`. Locally, verify with `npx tsc --noEmit`.
|
|
76
|
+
- Frontend usage of the package: `docs/agents/RELEASING.md` in
|
|
77
|
+
`studious-lms/studious` is the mirror of this skill from the consumer side.
|
|
78
|
+
|
|
79
|
+
## Testing
|
|
80
|
+
|
|
81
|
+
- `npm run test:unit` — unit tests (no database).
|
|
82
|
+
- `npm run test:run` — full suite; integration tests expect a test database
|
|
83
|
+
(`NODE_ENV=test`).
|
|
84
|
+
- Prisma client must be generated first: `npx prisma generate`.
|
|
@@ -18,6 +18,17 @@ export declare function findOrCreateScopeRoot(scope: CloudScope): Promise<{
|
|
|
18
18
|
schoolStorageId: string | null;
|
|
19
19
|
personalOwnerId: string | null;
|
|
20
20
|
}>;
|
|
21
|
+
/** Find a direct child folder by name, creating it if missing. */
|
|
22
|
+
export declare function findOrCreateChildFolderByName(parentFolderId: string, name: string): Promise<{
|
|
23
|
+
id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
scope: import(".prisma/client").$Enums.FolderScope;
|
|
26
|
+
parentFolderId: string | null;
|
|
27
|
+
color: string | null;
|
|
28
|
+
classId: string | null;
|
|
29
|
+
schoolStorageId: string | null;
|
|
30
|
+
personalOwnerId: string | null;
|
|
31
|
+
}>;
|
|
21
32
|
/**
|
|
22
33
|
* Walk up the folder tree until we reach a root with a scope owner. Used to
|
|
23
34
|
* permission-check sub-folders (which only carry a parentFolderId).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudStorage.d.ts","sourceRoot":"/","sources":["models/cloudStorage.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAazC,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,UAAU;;;;;;;;;GAqD5D;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAmB5B;AAmBD,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAgBtD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GASA;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAK9C;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAEhD;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAS1E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM;QAEQ,MAAM;UAAQ,MAAM;KAmI9C"}
|
|
1
|
+
{"version":3,"file":"cloudStorage.d.ts","sourceRoot":"/","sources":["models/cloudStorage.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAazC,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,UAAU;;;;;;;;;GAqD5D;AAED,kEAAkE;AAClE,wBAAsB,6BAA6B,CACjD,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,MAAM;;;;;;;;;GAcb;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAmB5B;AAmBD,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAgBtD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GASA;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAK9C;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAEhD;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAS1E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM;QAEQ,MAAM;UAAQ,MAAM;KAmI9C"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="0cfab180-d913-5bf0-94df-dbf4987546dc")}catch(e){}}();
|
|
3
3
|
import { prisma } from "../lib/prisma.js";
|
|
4
4
|
const folderSelect = {
|
|
5
5
|
id: true,
|
|
@@ -66,6 +66,22 @@ export async function findOrCreateScopeRoot(scope) {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
|
+
/** Find a direct child folder by name, creating it if missing. */
|
|
70
|
+
export async function findOrCreateChildFolderByName(parentFolderId, name) {
|
|
71
|
+
const existing = await prisma.folder.findFirst({
|
|
72
|
+
where: { parentFolderId, name },
|
|
73
|
+
select: folderSelect,
|
|
74
|
+
});
|
|
75
|
+
if (existing)
|
|
76
|
+
return existing;
|
|
77
|
+
return prisma.folder.create({
|
|
78
|
+
data: {
|
|
79
|
+
name,
|
|
80
|
+
parentFolder: { connect: { id: parentFolderId } },
|
|
81
|
+
},
|
|
82
|
+
select: folderSelect,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
69
85
|
/**
|
|
70
86
|
* Walk up the folder tree until we reach a root with a scope owner. Used to
|
|
71
87
|
* permission-check sub-folders (which only carry a parentFolderId).
|
|
@@ -292,4 +308,4 @@ export async function listFolderBreadcrumbs(folderId, viewerId) {
|
|
|
292
308
|
return breadcrumbs;
|
|
293
309
|
}
|
|
294
310
|
//# sourceMappingURL=cloudStorage.js.map
|
|
295
|
-
//# debugId=
|
|
311
|
+
//# debugId=0cfab180-d913-5bf0-94df-dbf4987546dc
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudStorage.js","sources":["models/cloudStorage.ts"],"sourceRoot":"/","sourcesContent":["/**\n * CloudStorage model — scope-aware folder helpers used by the institution and\n * personal cloud storage UI. Sits on top of the same Folder/File tables that\n * back class folders, but adds scope (school/personal) and ownership semantics.\n */\nimport type { FolderScope } from \"@prisma/client\";\nimport { prisma } from \"../lib/prisma.js\";\n\nexport type CloudScope =\n | { kind: \"CLASS\"; classId: string }\n | { kind: \"SCHOOL_STORAGE\"; schoolId: string }\n | { kind: \"PERSONAL\"; userId: string };\n\nconst folderSelect = {\n id: true,\n name: true,\n color: true,\n scope: true,\n classId: true,\n schoolStorageId: true,\n personalOwnerId: true,\n parentFolderId: true,\n} as const;\n\nexport async function findOrCreateScopeRoot(scope: CloudScope) {\n switch (scope.kind) {\n case \"CLASS\": {\n let root = await prisma.folder.findFirst({\n where: { classId: scope.classId, parentFolderId: null },\n });\n if (!root) {\n root = await prisma.folder.create({\n data: {\n name: \"Class Files\",\n scope: \"CLASS\" as FolderScope,\n class: { connect: { id: scope.classId } },\n },\n });\n } else if (root.scope !== \"CLASS\") {\n root = await prisma.folder.update({\n where: { id: root.id },\n data: { scope: \"CLASS\" as FolderScope },\n });\n }\n return root;\n }\n case \"SCHOOL_STORAGE\": {\n let root = await prisma.folder.findFirst({\n where: { schoolStorageId: scope.schoolId },\n });\n if (!root) {\n root = await prisma.folder.create({\n data: {\n name: \"Cloud Storage\",\n scope: \"SCHOOL_STORAGE\" as FolderScope,\n schoolStorage: { connect: { id: scope.schoolId } },\n },\n });\n }\n return root;\n }\n case \"PERSONAL\": {\n let root = await prisma.folder.findFirst({\n where: { personalOwnerId: scope.userId },\n });\n if (!root) {\n root = await prisma.folder.create({\n data: {\n name: \"My Files\",\n scope: \"PERSONAL\" as FolderScope,\n personalOwner: { connect: { id: scope.userId } },\n },\n });\n }\n return root;\n }\n }\n}\n\n/**\n * Walk up the folder tree until we reach a root with a scope owner. Used to\n * permission-check sub-folders (which only carry a parentFolderId).\n */\nexport async function resolveFolderScope(\n folderId: string\n): Promise<CloudScope | null> {\n let currentId: string | null = folderId;\n let visits = 0;\n while (currentId && visits < 64) {\n const folder = await prisma.folder.findUnique({\n where: { id: currentId },\n select: folderSelect,\n });\n if (!folder) return null;\n if (folder.classId) return { kind: \"CLASS\", classId: folder.classId };\n if (folder.schoolStorageId)\n return { kind: \"SCHOOL_STORAGE\", schoolId: folder.schoolStorageId };\n if (folder.personalOwnerId)\n return { kind: \"PERSONAL\", userId: folder.personalOwnerId };\n if (!folder.parentFolderId) return null;\n currentId = folder.parentFolderId;\n visits += 1;\n }\n return null;\n}\n\nconst fileSelect = {\n id: true,\n name: true,\n type: true,\n size: true,\n path: true,\n uploadedAt: true,\n user: { select: { id: true, username: true } },\n} as const;\n\nconst childFolderSelect = {\n id: true,\n name: true,\n color: true,\n _count: { select: { files: true, childFolders: true } },\n} as const;\n\nexport function findFolderWithContents(folderId: string) {\n return prisma.folder.findFirst({\n where: { id: folderId },\n include: {\n files: {\n where: { uploadStatus: \"COMPLETED\" },\n select: fileSelect,\n orderBy: { uploadedAt: \"desc\" },\n },\n childFolders: {\n select: childFolderSelect,\n orderBy: { name: \"asc\" },\n },\n parentFolder: { select: { id: true, name: true } },\n },\n });\n}\n\nexport function createSubFolder(data: {\n name: string;\n parentFolderId: string;\n color?: string;\n}) {\n return prisma.folder.create({\n data: {\n name: data.name,\n color: data.color,\n parentFolder: { connect: { id: data.parentFolderId } },\n },\n select: folderSelect,\n });\n}\n\nexport function findFolderById(folderId: string) {\n return prisma.folder.findUnique({\n where: { id: folderId },\n select: folderSelect,\n });\n}\n\nexport function deleteFolderById(folderId: string) {\n return prisma.folder.delete({ where: { id: folderId } });\n}\n\nexport function renameFolder(folderId: string, name: string, color?: string) {\n return prisma.folder.update({\n where: { id: folderId },\n data: {\n name,\n ...(color ? { color } : {}),\n },\n select: folderSelect,\n });\n}\n\n/**\n * Build the breadcrumb chain for a folder.\n *\n * Without `viewerId`: walks all the way up to the scope root (owner-style\n * view).\n *\n * With `viewerId`: walks up but stops at the highest folder the viewer is\n * actually granted on, OR at the scope root if the viewer is a scope member.\n * This is the Drive-style \"Shared with me\" behavior — a grantee never sees\n * ancestor folders they weren't shared on. The result for a grantee browsing\n * `Owner/PrivateA/SharedB/C` (granted on B) reads `SharedB / C` — the ancestor\n * `PrivateA` is correctly hidden.\n */\nexport async function listFolderBreadcrumbs(\n folderId: string,\n viewerId?: string\n) {\n const breadcrumbs: { id: string; name: string }[] = [];\n let currentId: string | null = folderId;\n let visits = 0;\n\n let viewerGrantedFolderIds: Set<string> | null = null;\n let viewerScopeMember = false;\n\n if (viewerId) {\n const grants = await prisma.permission.findMany({\n where: {\n OR: [\n { principalType: \"USER\", userId: viewerId },\n {\n principalType: \"TAG\",\n tag: { users: { some: { id: viewerId } } },\n },\n {\n principalType: \"CLASS\",\n class: {\n OR: [\n { teachers: { some: { id: viewerId } } },\n { students: { some: { id: viewerId } } },\n ],\n },\n },\n {\n principalType: \"CLASS_TEACHERS\",\n class: { teachers: { some: { id: viewerId } } },\n },\n {\n principalType: \"CLASS_STUDENTS\",\n class: { students: { some: { id: viewerId } } },\n },\n {\n principalType: \"DEPARTMENT\",\n department: { members: { some: { id: viewerId } } },\n },\n {\n principalType: \"SCHOOL\",\n school: {\n memberships: { some: { userId: viewerId, status: \"ACTIVE\" } },\n },\n },\n ],\n },\n select: { folderId: true },\n });\n viewerGrantedFolderIds = new Set(\n grants.map((g) => g.folderId).filter((x): x is string => Boolean(x))\n );\n\n // Determine if the viewer is a member of the *target* folder's scope —\n // if so, breadcrumbs go all the way to the scope root as normal.\n const scope = await resolveFolderScope(folderId);\n if (scope) {\n switch (scope.kind) {\n case \"CLASS\": {\n const c = await prisma.class.findFirst({\n where: {\n id: scope.classId,\n OR: [\n { teachers: { some: { id: viewerId } } },\n { students: { some: { id: viewerId } } },\n ],\n },\n select: { id: true },\n });\n viewerScopeMember = Boolean(c);\n if (!viewerScopeMember && scope.classId) {\n const klass = await prisma.class.findUnique({\n where: { id: scope.classId },\n select: { schoolId: true },\n });\n if (klass?.schoolId) {\n const m = await prisma.schoolMembership.findFirst({\n where: {\n schoolId: klass.schoolId,\n userId: viewerId,\n role: \"ADMIN\",\n status: \"ACTIVE\",\n },\n select: { id: true },\n });\n viewerScopeMember = Boolean(m);\n }\n }\n break;\n }\n case \"SCHOOL_STORAGE\": {\n const m = await prisma.schoolMembership.findFirst({\n where: {\n schoolId: scope.schoolId,\n userId: viewerId,\n status: \"ACTIVE\",\n },\n select: { id: true },\n });\n viewerScopeMember = Boolean(m);\n break;\n }\n case \"PERSONAL\":\n viewerScopeMember = scope.userId === viewerId;\n break;\n }\n }\n }\n\n while (currentId && visits < 64) {\n const folder = await prisma.folder.findUnique({\n where: { id: currentId },\n select: { id: true, name: true, parentFolderId: true },\n });\n if (!folder) break;\n breadcrumbs.unshift({ id: folder.id, name: folder.name });\n\n // Viewer-aware truncation: when the viewer is *not* a scope member but\n // is a grantee, stop at the highest granted ancestor (we walk up one\n // more step into the granted folder, then stop there).\n if (\n viewerId &&\n !viewerScopeMember &&\n viewerGrantedFolderIds &&\n viewerGrantedFolderIds.has(folder.id)\n ) {\n break;\n }\n\n currentId = folder.parentFolderId;\n visits += 1;\n }\n return breadcrumbs;\n}\n"],"names":[],"mappings":"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","debug_id":"26810bf7-c72f-5655-8994-96075d84b6fe"}
|
|
1
|
+
{"version":3,"file":"cloudStorage.js","sources":["models/cloudStorage.ts"],"sourceRoot":"/","sourcesContent":["/**\n * CloudStorage model — scope-aware folder helpers used by the institution and\n * personal cloud storage UI. Sits on top of the same Folder/File tables that\n * back class folders, but adds scope (school/personal) and ownership semantics.\n */\nimport type { FolderScope } from \"@prisma/client\";\nimport { prisma } from \"../lib/prisma.js\";\n\nexport type CloudScope =\n | { kind: \"CLASS\"; classId: string }\n | { kind: \"SCHOOL_STORAGE\"; schoolId: string }\n | { kind: \"PERSONAL\"; userId: string };\n\nconst folderSelect = {\n id: true,\n name: true,\n color: true,\n scope: true,\n classId: true,\n schoolStorageId: true,\n personalOwnerId: true,\n parentFolderId: true,\n} as const;\n\nexport async function findOrCreateScopeRoot(scope: CloudScope) {\n switch (scope.kind) {\n case \"CLASS\": {\n let root = await prisma.folder.findFirst({\n where: { classId: scope.classId, parentFolderId: null },\n });\n if (!root) {\n root = await prisma.folder.create({\n data: {\n name: \"Class Files\",\n scope: \"CLASS\" as FolderScope,\n class: { connect: { id: scope.classId } },\n },\n });\n } else if (root.scope !== \"CLASS\") {\n root = await prisma.folder.update({\n where: { id: root.id },\n data: { scope: \"CLASS\" as FolderScope },\n });\n }\n return root;\n }\n case \"SCHOOL_STORAGE\": {\n let root = await prisma.folder.findFirst({\n where: { schoolStorageId: scope.schoolId },\n });\n if (!root) {\n root = await prisma.folder.create({\n data: {\n name: \"Cloud Storage\",\n scope: \"SCHOOL_STORAGE\" as FolderScope,\n schoolStorage: { connect: { id: scope.schoolId } },\n },\n });\n }\n return root;\n }\n case \"PERSONAL\": {\n let root = await prisma.folder.findFirst({\n where: { personalOwnerId: scope.userId },\n });\n if (!root) {\n root = await prisma.folder.create({\n data: {\n name: \"My Files\",\n scope: \"PERSONAL\" as FolderScope,\n personalOwner: { connect: { id: scope.userId } },\n },\n });\n }\n return root;\n }\n }\n}\n\n/** Find a direct child folder by name, creating it if missing. */\nexport async function findOrCreateChildFolderByName(\n parentFolderId: string,\n name: string\n) {\n const existing = await prisma.folder.findFirst({\n where: { parentFolderId, name },\n select: folderSelect,\n });\n if (existing) return existing;\n return prisma.folder.create({\n data: {\n name,\n parentFolder: { connect: { id: parentFolderId } },\n },\n select: folderSelect,\n });\n}\n\n/**\n * Walk up the folder tree until we reach a root with a scope owner. Used to\n * permission-check sub-folders (which only carry a parentFolderId).\n */\nexport async function resolveFolderScope(\n folderId: string\n): Promise<CloudScope | null> {\n let currentId: string | null = folderId;\n let visits = 0;\n while (currentId && visits < 64) {\n const folder = await prisma.folder.findUnique({\n where: { id: currentId },\n select: folderSelect,\n });\n if (!folder) return null;\n if (folder.classId) return { kind: \"CLASS\", classId: folder.classId };\n if (folder.schoolStorageId)\n return { kind: \"SCHOOL_STORAGE\", schoolId: folder.schoolStorageId };\n if (folder.personalOwnerId)\n return { kind: \"PERSONAL\", userId: folder.personalOwnerId };\n if (!folder.parentFolderId) return null;\n currentId = folder.parentFolderId;\n visits += 1;\n }\n return null;\n}\n\nconst fileSelect = {\n id: true,\n name: true,\n type: true,\n size: true,\n path: true,\n uploadedAt: true,\n user: { select: { id: true, username: true } },\n} as const;\n\nconst childFolderSelect = {\n id: true,\n name: true,\n color: true,\n _count: { select: { files: true, childFolders: true } },\n} as const;\n\nexport function findFolderWithContents(folderId: string) {\n return prisma.folder.findFirst({\n where: { id: folderId },\n include: {\n files: {\n where: { uploadStatus: \"COMPLETED\" },\n select: fileSelect,\n orderBy: { uploadedAt: \"desc\" },\n },\n childFolders: {\n select: childFolderSelect,\n orderBy: { name: \"asc\" },\n },\n parentFolder: { select: { id: true, name: true } },\n },\n });\n}\n\nexport function createSubFolder(data: {\n name: string;\n parentFolderId: string;\n color?: string;\n}) {\n return prisma.folder.create({\n data: {\n name: data.name,\n color: data.color,\n parentFolder: { connect: { id: data.parentFolderId } },\n },\n select: folderSelect,\n });\n}\n\nexport function findFolderById(folderId: string) {\n return prisma.folder.findUnique({\n where: { id: folderId },\n select: folderSelect,\n });\n}\n\nexport function deleteFolderById(folderId: string) {\n return prisma.folder.delete({ where: { id: folderId } });\n}\n\nexport function renameFolder(folderId: string, name: string, color?: string) {\n return prisma.folder.update({\n where: { id: folderId },\n data: {\n name,\n ...(color ? { color } : {}),\n },\n select: folderSelect,\n });\n}\n\n/**\n * Build the breadcrumb chain for a folder.\n *\n * Without `viewerId`: walks all the way up to the scope root (owner-style\n * view).\n *\n * With `viewerId`: walks up but stops at the highest folder the viewer is\n * actually granted on, OR at the scope root if the viewer is a scope member.\n * This is the Drive-style \"Shared with me\" behavior — a grantee never sees\n * ancestor folders they weren't shared on. The result for a grantee browsing\n * `Owner/PrivateA/SharedB/C` (granted on B) reads `SharedB / C` — the ancestor\n * `PrivateA` is correctly hidden.\n */\nexport async function listFolderBreadcrumbs(\n folderId: string,\n viewerId?: string\n) {\n const breadcrumbs: { id: string; name: string }[] = [];\n let currentId: string | null = folderId;\n let visits = 0;\n\n let viewerGrantedFolderIds: Set<string> | null = null;\n let viewerScopeMember = false;\n\n if (viewerId) {\n const grants = await prisma.permission.findMany({\n where: {\n OR: [\n { principalType: \"USER\", userId: viewerId },\n {\n principalType: \"TAG\",\n tag: { users: { some: { id: viewerId } } },\n },\n {\n principalType: \"CLASS\",\n class: {\n OR: [\n { teachers: { some: { id: viewerId } } },\n { students: { some: { id: viewerId } } },\n ],\n },\n },\n {\n principalType: \"CLASS_TEACHERS\",\n class: { teachers: { some: { id: viewerId } } },\n },\n {\n principalType: \"CLASS_STUDENTS\",\n class: { students: { some: { id: viewerId } } },\n },\n {\n principalType: \"DEPARTMENT\",\n department: { members: { some: { id: viewerId } } },\n },\n {\n principalType: \"SCHOOL\",\n school: {\n memberships: { some: { userId: viewerId, status: \"ACTIVE\" } },\n },\n },\n ],\n },\n select: { folderId: true },\n });\n viewerGrantedFolderIds = new Set(\n grants.map((g) => g.folderId).filter((x): x is string => Boolean(x))\n );\n\n // Determine if the viewer is a member of the *target* folder's scope —\n // if so, breadcrumbs go all the way to the scope root as normal.\n const scope = await resolveFolderScope(folderId);\n if (scope) {\n switch (scope.kind) {\n case \"CLASS\": {\n const c = await prisma.class.findFirst({\n where: {\n id: scope.classId,\n OR: [\n { teachers: { some: { id: viewerId } } },\n { students: { some: { id: viewerId } } },\n ],\n },\n select: { id: true },\n });\n viewerScopeMember = Boolean(c);\n if (!viewerScopeMember && scope.classId) {\n const klass = await prisma.class.findUnique({\n where: { id: scope.classId },\n select: { schoolId: true },\n });\n if (klass?.schoolId) {\n const m = await prisma.schoolMembership.findFirst({\n where: {\n schoolId: klass.schoolId,\n userId: viewerId,\n role: \"ADMIN\",\n status: \"ACTIVE\",\n },\n select: { id: true },\n });\n viewerScopeMember = Boolean(m);\n }\n }\n break;\n }\n case \"SCHOOL_STORAGE\": {\n const m = await prisma.schoolMembership.findFirst({\n where: {\n schoolId: scope.schoolId,\n userId: viewerId,\n status: \"ACTIVE\",\n },\n select: { id: true },\n });\n viewerScopeMember = Boolean(m);\n break;\n }\n case \"PERSONAL\":\n viewerScopeMember = scope.userId === viewerId;\n break;\n }\n }\n }\n\n while (currentId && visits < 64) {\n const folder = await prisma.folder.findUnique({\n where: { id: currentId },\n select: { id: true, name: true, parentFolderId: true },\n });\n if (!folder) break;\n breadcrumbs.unshift({ id: folder.id, name: folder.name });\n\n // Viewer-aware truncation: when the viewer is *not* a scope member but\n // is a grantee, stop at the highest granted ancestor (we walk up one\n // more step into the granted folder, then stop there).\n if (\n viewerId &&\n !viewerScopeMember &&\n viewerGrantedFolderIds &&\n viewerGrantedFolderIds.has(folder.id)\n ) {\n break;\n }\n\n currentId = folder.parentFolderId;\n visits += 1;\n }\n return breadcrumbs;\n}\n"],"names":[],"mappings":"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","debug_id":"0cfab180-d913-5bf0-94df-dbf4987546dc"}
|
|
@@ -39,6 +39,7 @@ export declare function findConversationsByUserId(userId: string): import("@pris
|
|
|
39
39
|
content: string;
|
|
40
40
|
senderId: string;
|
|
41
41
|
conversationId: string;
|
|
42
|
+
aiFeedback: import(".prisma/client").$Enums.AiMessageFeedback | null;
|
|
42
43
|
runJobId: string | null;
|
|
43
44
|
})[];
|
|
44
45
|
} & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation.d.ts","sourceRoot":"/","sources":["models/conversation.ts"],"names":[],"mappings":"AAmCA,mEAAmE;AACnE,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM
|
|
1
|
+
{"version":3,"file":"conversation.d.ts","sourceRoot":"/","sources":["models/conversation.ts"],"names":[],"mappings":"AAmCA,mEAAmE;AACnE,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAQvD;AAED,sDAAsD;AACtD,wBAAgB,mBAAmB,CACjC,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,IAAI,qEASb;AAED,sDAAsD;AACtD,wBAAgB,mBAAmB,CACjC,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,IAAI,qEAYb;AAED,qDAAqD;AACrD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;UAKlD;AAED;;;;;;GAMG;AACH,wBAAsB,oCAAoC,CACxD,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM;;;;;;;;KA0Cd;AAED,wCAAwC;AACxC,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;MAUzD;AAED,0CAA0C;AAC1C,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,MAAM,EAAE;;;KAW9D;AAED,sDAAsD;AACtD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACvC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BA;AAED,iDAAiD;AACjD,wBAAgB,6BAA6B,CAC3C,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAuBf;AAED,sCAAsC;AACtC,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;CACd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAQA;AAED,yEAAyE;AACzE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAS5E;AAED,iDAAiD;AACjD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAS9E;AAED,2CAA2C;AAC3C,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAM9E"}
|
package/dist/models/labChat.d.ts
CHANGED
|
@@ -252,6 +252,45 @@ export declare function findLabChatById(labChatId: string): import("@prisma/clie
|
|
|
252
252
|
conversationId: string;
|
|
253
253
|
createdById: string;
|
|
254
254
|
}) | null>;
|
|
255
|
+
/** @returns Class context for starter prompt suggestions (teacher only). */
|
|
256
|
+
export declare function findClassContextForStarterPrompts(classId: string, userId: string): import("@prisma/client/runtime/library.js").DynamicModelExtensionFluentApi<import(".prisma/client").Prisma.TypeMap<import("@prisma/client/runtime/library.js").InternalArgs & {
|
|
257
|
+
result: {
|
|
258
|
+
userProfile: {
|
|
259
|
+
profilePicture: () => {
|
|
260
|
+
needs: {
|
|
261
|
+
profilePicture: true;
|
|
262
|
+
};
|
|
263
|
+
compute(profile: {
|
|
264
|
+
profilePicture: string | null;
|
|
265
|
+
}): string | null;
|
|
266
|
+
};
|
|
267
|
+
profilePictureThumbnail: () => {
|
|
268
|
+
needs: {
|
|
269
|
+
profilePictureThumbnail: true;
|
|
270
|
+
};
|
|
271
|
+
compute(profile: {
|
|
272
|
+
profilePictureThumbnail: string | null;
|
|
273
|
+
}): string | null;
|
|
274
|
+
};
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
model: {};
|
|
278
|
+
query: {};
|
|
279
|
+
client: {};
|
|
280
|
+
}, {}>, "Class", "findFirst", null> & import("@prisma/client/runtime/library.js").PrismaPromise<{
|
|
281
|
+
id: string;
|
|
282
|
+
name: string;
|
|
283
|
+
assignments: {
|
|
284
|
+
type: import(".prisma/client").$Enums.AssignmentType;
|
|
285
|
+
title: string;
|
|
286
|
+
dueDate: Date;
|
|
287
|
+
inProgress: boolean;
|
|
288
|
+
}[];
|
|
289
|
+
subject: string;
|
|
290
|
+
sections: {
|
|
291
|
+
name: string;
|
|
292
|
+
}[];
|
|
293
|
+
} | null>;
|
|
255
294
|
/** @returns Class if user is student, teacher, or school admin. */
|
|
256
295
|
export declare function findClassMembership(classId: string, userId: string): import("@prisma/client/runtime/library.js").DynamicModelExtensionFluentApi<import(".prisma/client").Prisma.TypeMap<import("@prisma/client/runtime/library.js").InternalArgs & {
|
|
257
296
|
result: {
|
|
@@ -316,6 +355,7 @@ export declare function findLabChatsByClass(classId: string): import("@prisma/cl
|
|
|
316
355
|
content: string;
|
|
317
356
|
senderId: string;
|
|
318
357
|
conversationId: string;
|
|
358
|
+
aiFeedback: import(".prisma/client").$Enums.AiMessageFeedback | null;
|
|
319
359
|
runJobId: string | null;
|
|
320
360
|
})[];
|
|
321
361
|
} & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"labChat.d.ts","sourceRoot":"/","sources":["models/labChat.ts"],"names":[],"mappings":"AAoDA,kEAAkE;AAClE,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAiC7E;AAED,0DAA0D;AAC1D,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAUpE;AAED,kEAAkE;AAClE,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAYrE;AAED,+BAA+B;AAC/B,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAKhD;AAED,mEAAmE;AACnE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAOlE;AAED,sCAAsC;AACtC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM
|
|
1
|
+
{"version":3,"file":"labChat.d.ts","sourceRoot":"/","sources":["models/labChat.ts"],"names":[],"mappings":"AAoDA,kEAAkE;AAClE,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAiC7E;AAED,0DAA0D;AAC1D,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAUpE;AAED,kEAAkE;AAClE,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAYrE;AAED,+BAA+B;AAC/B,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAKhD;AAED,4EAA4E;AAC5E,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA6Bf;AAED,mEAAmE;AACnE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAOlE;AAED,sCAAsC;AACtC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAwClD;AAED,8DAA8D;AAC9D,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAYnE;AAED,sDAAsD;AACtD,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAOrE;AAED,4DAA4D;AAC5D,wBAAgB,wBAAwB,CACtC,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,MAAM,EAAE;;;;;;;;;KAQlB"}
|
package/dist/models/labChat.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Lab chat model – AI lab conversations, members, posts. Teacher/student access.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
5
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="f0a38650-ffe1-5bd7-ae39-8fcbfbd0dc27")}catch(e){}}();
|
|
6
6
|
import { prisma } from "../lib/prisma.js";
|
|
7
7
|
import { classMemberOrSchoolAdminWhere, classTeacherOrSchoolAdminWhere, } from "./access.js";
|
|
8
8
|
const labChatInclude = {
|
|
@@ -115,6 +115,36 @@ export function findLabChatById(labChatId) {
|
|
|
115
115
|
include: labChatInclude,
|
|
116
116
|
});
|
|
117
117
|
}
|
|
118
|
+
/** @returns Class context for starter prompt suggestions (teacher only). */
|
|
119
|
+
export function findClassContextForStarterPrompts(classId, userId) {
|
|
120
|
+
return prisma.class.findFirst({
|
|
121
|
+
where: {
|
|
122
|
+
id: classId,
|
|
123
|
+
...classTeacherOrSchoolAdminWhere(userId),
|
|
124
|
+
},
|
|
125
|
+
select: {
|
|
126
|
+
id: true,
|
|
127
|
+
name: true,
|
|
128
|
+
subject: true,
|
|
129
|
+
sections: {
|
|
130
|
+
select: { name: true },
|
|
131
|
+
orderBy: { order: "asc" },
|
|
132
|
+
take: 5,
|
|
133
|
+
},
|
|
134
|
+
assignments: {
|
|
135
|
+
where: { template: false },
|
|
136
|
+
select: {
|
|
137
|
+
title: true,
|
|
138
|
+
type: true,
|
|
139
|
+
dueDate: true,
|
|
140
|
+
inProgress: true,
|
|
141
|
+
},
|
|
142
|
+
orderBy: { createdAt: "desc" },
|
|
143
|
+
take: 10,
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
}
|
|
118
148
|
/** @returns Class if user is student, teacher, or school admin. */
|
|
119
149
|
export function findClassMembership(classId, userId) {
|
|
120
150
|
return prisma.class.findFirst({
|
|
@@ -199,4 +229,4 @@ export function findMentionedMemberships(conversationId, userIds) {
|
|
|
199
229
|
});
|
|
200
230
|
}
|
|
201
231
|
//# sourceMappingURL=labChat.js.map
|
|
202
|
-
//# debugId=
|
|
232
|
+
//# debugId=f0a38650-ffe1-5bd7-ae39-8fcbfbd0dc27
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"labChat.js","sources":["models/labChat.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Lab chat model – AI lab conversations, members, posts. Teacher/student access.\n */\nimport { prisma } from \"../lib/prisma.js\";\nimport {\n classMemberOrSchoolAdminWhere,\n classTeacherOrSchoolAdminWhere,\n} from \"./access.js\";\n\nconst labChatInclude = {\n conversation: {\n include: {\n members: {\n include: {\n user: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n },\n },\n },\n },\n createdBy: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n class: {\n select: {\n id: true,\n name: true,\n subject: true,\n section: true,\n },\n },\n};\n\n/** @returns Class with students and teachers for lab creation. */\nexport function findClassWithTeachersForLab(classId: string, teacherId: string) {\n return prisma.class.findFirst({\n where: {\n id: classId,\n ...classTeacherOrSchoolAdminWhere(teacherId),\n },\n include: {\n students: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n teachers: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n },\n });\n}\n\n/** @returns Lab chat if user is a conversation member. */\nexport function findLabChatByMember(labChatId: string, userId: string) {\n return prisma.labChat.findFirst({\n where: {\n id: labChatId,\n conversation: {\n members: { some: { userId } },\n },\n },\n include: labChatInclude,\n });\n}\n\n/** @returns Lab chat if user is class teacher or school admin. */\nexport function findLabChatByTeacher(labChatId: string, userId: string) {\n return prisma.labChat.findFirst({\n where: {\n id: labChatId,\n class: {\n ...classTeacherOrSchoolAdminWhere(userId),\n },\n },\n include: {\n conversation: { select: { id: true } },\n },\n });\n}\n\n/** @returns Lab chat by ID. */\nexport function findLabChatById(labChatId: string) {\n return prisma.labChat.findFirst({\n where: { id: labChatId },\n include: labChatInclude,\n });\n}\n\n/** @returns Class if user is student, teacher, or school admin. */\nexport function findClassMembership(classId: string, userId: string) {\n return prisma.class.findFirst({\n where: {\n id: classId,\n ...classMemberOrSchoolAdminWhere(userId),\n },\n });\n}\n\n/** @returns Lab chats for a class. */\nexport function findLabChatsByClass(classId: string) {\n return prisma.labChat.findMany({\n where: { classId },\n include: {\n createdBy: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n },\n },\n },\n },\n conversation: {\n include: {\n messages: {\n orderBy: { createdAt: \"desc\" as const },\n take: 1,\n include: {\n sender: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n },\n },\n },\n },\n },\n },\n _count: { select: { messages: true } },\n },\n },\n },\n orderBy: { createdAt: \"desc\" as const },\n });\n}\n\n/** @returns Lab chat if user can post (member or teacher). */\nexport function findLabChatForPost(labChatId: string, userId: string) {\n return prisma.labChat.findFirst({\n where: {\n id: labChatId,\n conversation: {\n members: { some: { userId } },\n },\n },\n include: {\n conversation: { select: { id: true } },\n },\n });\n}\n\n/** @returns Lab chat if user can delete (teacher). */\nexport function findLabChatForDelete(labChatId: string, userId: string) {\n return prisma.labChat.findFirst({\n where: {\n id: labChatId,\n createdById: userId,\n },\n });\n}\n\n/** @returns Conversation members for mentioned user IDs. */\nexport function findMentionedMemberships(\n conversationId: string,\n userIds: string[]\n) {\n return prisma.conversationMember.findMany({\n where: {\n conversationId,\n userId: { in: userIds },\n },\n });\n}\n"],"names":[],"mappings":"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","debug_id":"f519e035-445f-5c41-80c4-de7dd81d94e2"}
|
|
1
|
+
{"version":3,"file":"labChat.js","sources":["models/labChat.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Lab chat model – AI lab conversations, members, posts. Teacher/student access.\n */\nimport { prisma } from \"../lib/prisma.js\";\nimport {\n classMemberOrSchoolAdminWhere,\n classTeacherOrSchoolAdminWhere,\n} from \"./access.js\";\n\nconst labChatInclude = {\n conversation: {\n include: {\n members: {\n include: {\n user: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n },\n },\n },\n },\n createdBy: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n class: {\n select: {\n id: true,\n name: true,\n subject: true,\n section: true,\n },\n },\n};\n\n/** @returns Class with students and teachers for lab creation. */\nexport function findClassWithTeachersForLab(classId: string, teacherId: string) {\n return prisma.class.findFirst({\n where: {\n id: classId,\n ...classTeacherOrSchoolAdminWhere(teacherId),\n },\n include: {\n students: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n teachers: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n },\n });\n}\n\n/** @returns Lab chat if user is a conversation member. */\nexport function findLabChatByMember(labChatId: string, userId: string) {\n return prisma.labChat.findFirst({\n where: {\n id: labChatId,\n conversation: {\n members: { some: { userId } },\n },\n },\n include: labChatInclude,\n });\n}\n\n/** @returns Lab chat if user is class teacher or school admin. */\nexport function findLabChatByTeacher(labChatId: string, userId: string) {\n return prisma.labChat.findFirst({\n where: {\n id: labChatId,\n class: {\n ...classTeacherOrSchoolAdminWhere(userId),\n },\n },\n include: {\n conversation: { select: { id: true } },\n },\n });\n}\n\n/** @returns Lab chat by ID. */\nexport function findLabChatById(labChatId: string) {\n return prisma.labChat.findFirst({\n where: { id: labChatId },\n include: labChatInclude,\n });\n}\n\n/** @returns Class context for starter prompt suggestions (teacher only). */\nexport function findClassContextForStarterPrompts(\n classId: string,\n userId: string\n) {\n return prisma.class.findFirst({\n where: {\n id: classId,\n ...classTeacherOrSchoolAdminWhere(userId),\n },\n select: {\n id: true,\n name: true,\n subject: true,\n sections: {\n select: { name: true },\n orderBy: { order: \"asc\" as const },\n take: 5,\n },\n assignments: {\n where: { template: false },\n select: {\n title: true,\n type: true,\n dueDate: true,\n inProgress: true,\n },\n orderBy: { createdAt: \"desc\" as const },\n take: 10,\n },\n },\n });\n}\n\n/** @returns Class if user is student, teacher, or school admin. */\nexport function findClassMembership(classId: string, userId: string) {\n return prisma.class.findFirst({\n where: {\n id: classId,\n ...classMemberOrSchoolAdminWhere(userId),\n },\n });\n}\n\n/** @returns Lab chats for a class. */\nexport function findLabChatsByClass(classId: string) {\n return prisma.labChat.findMany({\n where: { classId },\n include: {\n createdBy: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n },\n },\n },\n },\n conversation: {\n include: {\n messages: {\n orderBy: { createdAt: \"desc\" as const },\n take: 1,\n include: {\n sender: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n },\n },\n },\n },\n },\n },\n _count: { select: { messages: true } },\n },\n },\n },\n orderBy: { createdAt: \"desc\" as const },\n });\n}\n\n/** @returns Lab chat if user can post (member or teacher). */\nexport function findLabChatForPost(labChatId: string, userId: string) {\n return prisma.labChat.findFirst({\n where: {\n id: labChatId,\n conversation: {\n members: { some: { userId } },\n },\n },\n include: {\n conversation: { select: { id: true } },\n },\n });\n}\n\n/** @returns Lab chat if user can delete (teacher). */\nexport function findLabChatForDelete(labChatId: string, userId: string) {\n return prisma.labChat.findFirst({\n where: {\n id: labChatId,\n createdById: userId,\n },\n });\n}\n\n/** @returns Conversation members for mentioned user IDs. */\nexport function findMentionedMemberships(\n conversationId: string,\n userIds: string[]\n) {\n return prisma.conversationMember.findMany({\n where: {\n conversationId,\n userId: { in: userIds },\n },\n });\n}\n"],"names":[],"mappings":"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","debug_id":"f0a38650-ffe1-5bd7-ae39-8fcbfbd0dc27"}
|
|
@@ -141,6 +141,41 @@ export declare function searchMarketplaceListings(input: {
|
|
|
141
141
|
})[];
|
|
142
142
|
nextCursor: string | undefined;
|
|
143
143
|
}>;
|
|
144
|
+
export declare function updateMarketplaceListing(id: string, data: {
|
|
145
|
+
title?: string;
|
|
146
|
+
description?: string | null;
|
|
147
|
+
visibility?: MarketplaceVisibility;
|
|
148
|
+
tags?: string[];
|
|
149
|
+
}): Promise<{
|
|
150
|
+
school: {
|
|
151
|
+
id: string;
|
|
152
|
+
name: string;
|
|
153
|
+
} | null;
|
|
154
|
+
author: {
|
|
155
|
+
id: string;
|
|
156
|
+
username: string;
|
|
157
|
+
profile: {
|
|
158
|
+
displayName: string | null;
|
|
159
|
+
} | null;
|
|
160
|
+
};
|
|
161
|
+
} & {
|
|
162
|
+
type: import(".prisma/client").$Enums.MarketplaceListingType;
|
|
163
|
+
payload: Prisma.JsonValue;
|
|
164
|
+
id: string;
|
|
165
|
+
createdAt: Date;
|
|
166
|
+
updatedAt: Date;
|
|
167
|
+
schoolId: string | null;
|
|
168
|
+
tags: string[];
|
|
169
|
+
description: string | null;
|
|
170
|
+
title: string;
|
|
171
|
+
worksheetId: string | null;
|
|
172
|
+
markSchemeId: string | null;
|
|
173
|
+
authorId: string;
|
|
174
|
+
fileId: string | null;
|
|
175
|
+
visibility: import(".prisma/client").$Enums.MarketplaceVisibility;
|
|
176
|
+
reportCount: number;
|
|
177
|
+
isHidden: boolean;
|
|
178
|
+
}>;
|
|
144
179
|
export declare function incrementMarketplaceReportCount(id: string): Promise<{
|
|
145
180
|
id: string;
|
|
146
181
|
reportCount: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"marketplace.d.ts","sourceRoot":"/","sources":["models/marketplace.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EACV,sBAAsB,EACtB,qBAAqB,EACrB,MAAM,EACP,MAAM,gBAAgB,CAAC;AAGxB,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;CAS5B,CAAC;AAEX,MAAM,MAAM,+BAA+B,GAAG,MAAM,CAAC,4BAA4B,CAAC;IAChF,OAAO,EAAE,OAAO,yBAAyB,CAAC;CAC3C,CAAC,CAAC;AAEH,wBAAsB,wBAAwB,CAAC,IAAI,EAAE;IACnD,IAAI,EAAE,sBAAsB,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,qBAAqB,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC,cAAc,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiBA;AAED,wBAAsB,0BAA0B,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAK1D;AAED,wBAAsB,yBAAyB,CAAC,KAAK,EAAE;IACrD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,IAAI,CAAC,EAAE,sBAAsB,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DA;AAED,wBAAsB,+BAA+B,CAAC,EAAE,EAAE,MAAM;;;;GAM/D;AAED,wBAAsB,2BAA2B,CAC/C,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAOlB;AAED,wBAAsB,wBAAwB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,uEAI1E"}
|
|
1
|
+
{"version":3,"file":"marketplace.d.ts","sourceRoot":"/","sources":["models/marketplace.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EACV,sBAAsB,EACtB,qBAAqB,EACrB,MAAM,EACP,MAAM,gBAAgB,CAAC;AAGxB,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;CAS5B,CAAC;AAEX,MAAM,MAAM,+BAA+B,GAAG,MAAM,CAAC,4BAA4B,CAAC;IAChF,OAAO,EAAE,OAAO,yBAAyB,CAAC;CAC3C,CAAC,CAAC;AAEH,wBAAsB,wBAAwB,CAAC,IAAI,EAAE;IACnD,IAAI,EAAE,sBAAsB,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,qBAAqB,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC,cAAc,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiBA;AAED,wBAAsB,0BAA0B,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAK1D;AAED,wBAAsB,yBAAyB,CAAC,KAAK,EAAE;IACrD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,IAAI,CAAC,EAAE,sBAAsB,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DA;AAED,wBAAsB,wBAAwB,CAC5C,EAAE,EAAE,MAAM,EACV,IAAI,EAAE;IACJ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,CAAC,EAAE,qBAAqB,CAAC;IACnC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAOF;AAED,wBAAsB,+BAA+B,CAAC,EAAE,EAAE,MAAM;;;;GAM/D;AAED,wBAAsB,2BAA2B,CAC/C,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAOlB;AAED,wBAAsB,wBAAwB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,uEAI1E"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="e56099a1-dd5a-518e-8901-edc3da74c70d")}catch(e){}}();
|
|
3
3
|
import { prisma } from "../lib/prisma.js";
|
|
4
4
|
export const marketplaceListingInclude = {
|
|
5
5
|
author: {
|
|
@@ -93,6 +93,13 @@ export async function searchMarketplaceListings(input) {
|
|
|
93
93
|
}
|
|
94
94
|
return { items: rows, nextCursor };
|
|
95
95
|
}
|
|
96
|
+
export async function updateMarketplaceListing(id, data) {
|
|
97
|
+
return prisma.marketplaceListing.update({
|
|
98
|
+
where: { id },
|
|
99
|
+
data,
|
|
100
|
+
include: marketplaceListingInclude,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
96
103
|
export async function incrementMarketplaceReportCount(id) {
|
|
97
104
|
return prisma.marketplaceListing.update({
|
|
98
105
|
where: { id },
|
|
@@ -113,4 +120,4 @@ export async function deleteMarketplaceListing(id, authorId) {
|
|
|
113
120
|
});
|
|
114
121
|
}
|
|
115
122
|
//# sourceMappingURL=marketplace.js.map
|
|
116
|
-
//# debugId=
|
|
123
|
+
//# debugId=e56099a1-dd5a-518e-8901-edc3da74c70d
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"marketplace.js","sources":["models/marketplace.ts"],"sourceRoot":"/","sourcesContent":["/**\n * MarketplaceListing data access — B1 v1 (markschemes, worksheets, files).\n * Isolated Marketplace* tables only; no Attendance/Fee coupling.\n */\nimport type {\n MarketplaceListingType,\n MarketplaceVisibility,\n Prisma,\n} from \"@prisma/client\";\nimport { prisma } from \"../lib/prisma.js\";\n\nexport const marketplaceListingInclude = {\n author: {\n select: {\n id: true,\n username: true,\n profile: { select: { displayName: true } },\n },\n },\n school: { select: { id: true, name: true } },\n} as const;\n\nexport type MarketplaceListingWithRelations = Prisma.MarketplaceListingGetPayload<{\n include: typeof marketplaceListingInclude;\n}>;\n\nexport async function createMarketplaceListing(data: {\n type: MarketplaceListingType;\n title: string;\n description?: string | null;\n authorId: string;\n schoolId?: string | null;\n visibility: MarketplaceVisibility;\n markSchemeId?: string | null;\n worksheetId?: string | null;\n fileId?: string | null;\n payload: Prisma.InputJsonValue;\n tags?: string[];\n}) {\n return prisma.marketplaceListing.create({\n data: {\n type: data.type,\n title: data.title,\n description: data.description ?? null,\n authorId: data.authorId,\n schoolId: data.schoolId ?? null,\n visibility: data.visibility,\n markSchemeId: data.markSchemeId ?? null,\n worksheetId: data.worksheetId ?? null,\n fileId: data.fileId ?? null,\n payload: data.payload,\n tags: data.tags ?? [],\n },\n include: marketplaceListingInclude,\n });\n}\n\nexport async function findMarketplaceListingById(id: string) {\n return prisma.marketplaceListing.findUnique({\n where: { id },\n include: marketplaceListingInclude,\n });\n}\n\nexport async function searchMarketplaceListings(input: {\n schoolIds: string[];\n type?: MarketplaceListingType;\n search?: string;\n tags?: string[];\n cursor?: string;\n limit: number;\n /** When true, include the caller's own hidden listings (for authors). */\n authorId?: string;\n /** When set, restrict the entire result set (including PUBLIC listings) to this school. */\n scopeSchoolId?: string;\n}) {\n const search = input.search?.trim();\n const where: Prisma.MarketplaceListingWhereInput = {\n AND: [\n ...(input.scopeSchoolId ? [{ schoolId: input.scopeSchoolId }] : []),\n {\n OR: [\n { visibility: \"PUBLIC\", isHidden: false },\n {\n visibility: \"SCHOOL\",\n isHidden: false,\n schoolId: { in: input.schoolIds },\n },\n // Authors can always see their own listings (including hidden).\n ...(input.authorId ? [{ authorId: input.authorId }] : []),\n ],\n },\n ...(input.type ? [{ type: input.type }] : []),\n ...(input.tags?.length\n ? [{ tags: { hasSome: input.tags } }]\n : []),\n ...(search\n ? [\n {\n OR: [\n { title: { contains: search, mode: \"insensitive\" as const } },\n {\n description: {\n contains: search,\n mode: \"insensitive\" as const,\n },\n },\n { tags: { has: search } },\n ],\n },\n ]\n : []),\n ],\n };\n\n const rows = await prisma.marketplaceListing.findMany({\n where,\n include: marketplaceListingInclude,\n orderBy: [{ createdAt: \"desc\" }, { id: \"desc\" }],\n take: input.limit + 1,\n ...(input.cursor\n ? {\n cursor: { id: input.cursor },\n skip: 1,\n }\n : {}),\n });\n\n let nextCursor: string | undefined;\n if (rows.length > input.limit) {\n const next = rows.pop();\n nextCursor = next?.id;\n }\n\n return { items: rows, nextCursor };\n}\n\nexport async function incrementMarketplaceReportCount(id: string) {\n return prisma.marketplaceListing.update({\n where: { id },\n data: { reportCount: { increment: 1 } },\n select: { id: true, reportCount: true, isHidden: true },\n });\n}\n\nexport async function setMarketplaceListingHidden(\n id: string,\n isHidden: boolean\n) {\n return prisma.marketplaceListing.update({\n where: { id },\n data: { isHidden },\n include: marketplaceListingInclude,\n });\n}\n\nexport async function deleteMarketplaceListing(id: string, authorId: string) {\n return prisma.marketplaceListing.deleteMany({\n where: { id, authorId },\n });\n}\n"],"names":[],"mappings":"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","debug_id":"
|
|
1
|
+
{"version":3,"file":"marketplace.js","sources":["models/marketplace.ts"],"sourceRoot":"/","sourcesContent":["/**\n * MarketplaceListing data access — B1 v1 (markschemes, worksheets, files).\n * Isolated Marketplace* tables only; no Attendance/Fee coupling.\n */\nimport type {\n MarketplaceListingType,\n MarketplaceVisibility,\n Prisma,\n} from \"@prisma/client\";\nimport { prisma } from \"../lib/prisma.js\";\n\nexport const marketplaceListingInclude = {\n author: {\n select: {\n id: true,\n username: true,\n profile: { select: { displayName: true } },\n },\n },\n school: { select: { id: true, name: true } },\n} as const;\n\nexport type MarketplaceListingWithRelations = Prisma.MarketplaceListingGetPayload<{\n include: typeof marketplaceListingInclude;\n}>;\n\nexport async function createMarketplaceListing(data: {\n type: MarketplaceListingType;\n title: string;\n description?: string | null;\n authorId: string;\n schoolId?: string | null;\n visibility: MarketplaceVisibility;\n markSchemeId?: string | null;\n worksheetId?: string | null;\n fileId?: string | null;\n payload: Prisma.InputJsonValue;\n tags?: string[];\n}) {\n return prisma.marketplaceListing.create({\n data: {\n type: data.type,\n title: data.title,\n description: data.description ?? null,\n authorId: data.authorId,\n schoolId: data.schoolId ?? null,\n visibility: data.visibility,\n markSchemeId: data.markSchemeId ?? null,\n worksheetId: data.worksheetId ?? null,\n fileId: data.fileId ?? null,\n payload: data.payload,\n tags: data.tags ?? [],\n },\n include: marketplaceListingInclude,\n });\n}\n\nexport async function findMarketplaceListingById(id: string) {\n return prisma.marketplaceListing.findUnique({\n where: { id },\n include: marketplaceListingInclude,\n });\n}\n\nexport async function searchMarketplaceListings(input: {\n schoolIds: string[];\n type?: MarketplaceListingType;\n search?: string;\n tags?: string[];\n cursor?: string;\n limit: number;\n /** When true, include the caller's own hidden listings (for authors). */\n authorId?: string;\n /** When set, restrict the entire result set (including PUBLIC listings) to this school. */\n scopeSchoolId?: string;\n}) {\n const search = input.search?.trim();\n const where: Prisma.MarketplaceListingWhereInput = {\n AND: [\n ...(input.scopeSchoolId ? [{ schoolId: input.scopeSchoolId }] : []),\n {\n OR: [\n { visibility: \"PUBLIC\", isHidden: false },\n {\n visibility: \"SCHOOL\",\n isHidden: false,\n schoolId: { in: input.schoolIds },\n },\n // Authors can always see their own listings (including hidden).\n ...(input.authorId ? [{ authorId: input.authorId }] : []),\n ],\n },\n ...(input.type ? [{ type: input.type }] : []),\n ...(input.tags?.length\n ? [{ tags: { hasSome: input.tags } }]\n : []),\n ...(search\n ? [\n {\n OR: [\n { title: { contains: search, mode: \"insensitive\" as const } },\n {\n description: {\n contains: search,\n mode: \"insensitive\" as const,\n },\n },\n { tags: { has: search } },\n ],\n },\n ]\n : []),\n ],\n };\n\n const rows = await prisma.marketplaceListing.findMany({\n where,\n include: marketplaceListingInclude,\n orderBy: [{ createdAt: \"desc\" }, { id: \"desc\" }],\n take: input.limit + 1,\n ...(input.cursor\n ? {\n cursor: { id: input.cursor },\n skip: 1,\n }\n : {}),\n });\n\n let nextCursor: string | undefined;\n if (rows.length > input.limit) {\n const next = rows.pop();\n nextCursor = next?.id;\n }\n\n return { items: rows, nextCursor };\n}\n\nexport async function updateMarketplaceListing(\n id: string,\n data: {\n title?: string;\n description?: string | null;\n visibility?: MarketplaceVisibility;\n tags?: string[];\n }\n) {\n return prisma.marketplaceListing.update({\n where: { id },\n data,\n include: marketplaceListingInclude,\n });\n}\n\nexport async function incrementMarketplaceReportCount(id: string) {\n return prisma.marketplaceListing.update({\n where: { id },\n data: { reportCount: { increment: 1 } },\n select: { id: true, reportCount: true, isHidden: true },\n });\n}\n\nexport async function setMarketplaceListingHidden(\n id: string,\n isHidden: boolean\n) {\n return prisma.marketplaceListing.update({\n where: { id },\n data: { isHidden },\n include: marketplaceListingInclude,\n });\n}\n\nexport async function deleteMarketplaceListing(id: string, authorId: string) {\n return prisma.marketplaceListing.deleteMany({\n where: { id, authorId },\n });\n}\n"],"names":[],"mappings":"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","debug_id":"e56099a1-dd5a-518e-8901-edc3da74c70d"}
|
package/dist/models/message.d.ts
CHANGED
|
@@ -107,6 +107,7 @@ export declare function findMessages(conversationId: string, options: {
|
|
|
107
107
|
content: string;
|
|
108
108
|
senderId: string;
|
|
109
109
|
conversationId: string;
|
|
110
|
+
aiFeedback: import(".prisma/client").$Enums.AiMessageFeedback | null;
|
|
110
111
|
runJobId: string | null;
|
|
111
112
|
})[]>;
|
|
112
113
|
/** @returns Memberships for mentioned users. */
|
|
@@ -162,6 +163,7 @@ export declare function findMessageById(messageId: string): import("@prisma/clie
|
|
|
162
163
|
content: string;
|
|
163
164
|
senderId: string;
|
|
164
165
|
conversationId: string;
|
|
166
|
+
aiFeedback: import(".prisma/client").$Enums.AiMessageFeedback | null;
|
|
165
167
|
runJobId: string | null;
|
|
166
168
|
}) | null>;
|
|
167
169
|
/** @returns Message by ID with minimal sender. */
|
|
@@ -202,6 +204,7 @@ export declare function findMessageByIdMinimal(messageId: string): import("@pris
|
|
|
202
204
|
content: string;
|
|
203
205
|
senderId: string;
|
|
204
206
|
conversationId: string;
|
|
207
|
+
aiFeedback: import(".prisma/client").$Enums.AiMessageFeedback | null;
|
|
205
208
|
runJobId: string | null;
|
|
206
209
|
}) | null>;
|
|
207
210
|
/** Count messages in conversation after date, excluding sender's own. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message.d.ts","sourceRoot":"/","sources":["models/message.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAG7C;;;;GAIG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;CAUQ,CAAC;AA8CvC,oDAAoD;AACpD,wBAAgB,0BAA0B,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAOhF;AAED,gEAAgE;AAChE,wBAAgB,YAAY,CAC1B,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE;IACP,MAAM,CAAC,EAAE,IAAI,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf
|
|
1
|
+
{"version":3,"file":"message.d.ts","sourceRoot":"/","sources":["models/message.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAG7C;;;;GAIG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;CAUQ,CAAC;AA8CvC,oDAAoD;AACpD,wBAAgB,0BAA0B,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAOhF;AAED,gEAAgE;AAChE,wBAAgB,YAAY,CAC1B,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE;IACP,MAAM,CAAC,EAAE,IAAI,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAaF;AAED,gDAAgD;AAChD,wBAAgB,wBAAwB,CACtC,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,MAAM,EAAE;;;;;;;;;KAQlB;AAED,0CAA0C;AAC1C,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAShD;AAED,kDAAkD;AAClD,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WASvD;AAED,yEAAyE;AACzE,wBAAgB,mBAAmB,CACjC,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,IAAI,qEASb;AAED,yDAAyD;AACzD,wBAAgB,mBAAmB,CACjC,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,IAAI,qEAYb"}
|