├── .babelrc
├── .github
└── workflows
│ ├── deploy_docs.yml
│ ├── publish.yml
│ └── test.yml
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── assets
└── logo.svg
├── docs
├── .gitignore
├── README.md
├── babel.config.js
├── docs
│ ├── contributing
│ │ ├── _category_.json
│ │ ├── docs.md
│ │ ├── general.md
│ │ └── sdk.md
│ ├── example_projects.md
│ ├── getting-started
│ │ ├── _category_.json
│ │ ├── installation.md
│ │ └── usage.md
│ ├── methods
│ │ ├── _category_.json
│ │ ├── get_attendance.md
│ │ ├── get_exams.md
│ │ ├── get_grades.md
│ │ ├── get_homework.md
│ │ ├── get_lessons.md
│ │ ├── get_lucky_number.md
│ │ ├── get_message_boxes.md
│ │ ├── get_messages.md
│ │ ├── get_students.md
│ │ └── select_student.md
│ └── models
│ │ ├── _category_.json
│ │ ├── address.md
│ │ ├── attendance.md
│ │ ├── date_time.md
│ │ ├── exam.md
│ │ ├── grade.md
│ │ ├── grade_category.md
│ │ ├── grade_column.md
│ │ ├── homework.md
│ │ ├── lesson.md
│ │ ├── lesson_room.md
│ │ ├── lucky_number.md
│ │ ├── message.md
│ │ ├── message_box.md
│ │ ├── period.md
│ │ ├── presence_type.md
│ │ ├── pupil.md
│ │ ├── room.md
│ │ ├── school.md
│ │ ├── student.md
│ │ ├── subject.md
│ │ ├── teacher.md
│ │ ├── team_class.md
│ │ ├── team_virtual.md
│ │ ├── time_slot.md
│ │ └── unit.md
├── docusaurus.config.ts
├── package.json
├── sidebars.ts
├── src
│ ├── components
│ │ └── HomepageFeatures
│ │ │ ├── index.tsx
│ │ │ └── styles.module.css
│ ├── css
│ │ └── custom.css
│ └── pages
│ │ ├── index.module.css
│ │ ├── index.tsx
│ │ └── markdown-page.md
├── static
│ ├── .nojekyll
│ └── img
│ │ ├── docusaurus-social-card.jpg
│ │ ├── docusaurus.png
│ │ ├── favicon.ico
│ │ ├── logo.svg
│ │ ├── undraw_docusaurus_mountain.svg
│ │ ├── undraw_docusaurus_react.svg
│ │ └── undraw_docusaurus_tree.svg
└── tsconfig.json
├── package.json
├── rollup.config.mjs
├── src
├── api.ts
├── apiHelper.ts
├── endpoints.ts
├── index.ts
├── keystore.ts
├── models
│ ├── account.ts
│ ├── address.ts
│ ├── attachment.ts
│ ├── attendance.ts
│ ├── changedLesson.ts
│ ├── dateTime.ts
│ ├── exam.ts
│ ├── grade.ts
│ ├── gradeCategory.ts
│ ├── gradeColumn.ts
│ ├── homework.ts
│ ├── index.ts
│ ├── lesson.ts
│ ├── lessonChanges.ts
│ ├── lessonRoom.ts
│ ├── luckyNumber.ts
│ ├── message.ts
│ ├── messageBox.ts
│ ├── period.ts
│ ├── presenceType.ts
│ ├── pupil.ts
│ ├── school.ts
│ ├── student.ts
│ ├── subject.ts
│ ├── teacher.ts
│ ├── teamClass.ts
│ ├── teamVirtual.ts
│ ├── timeSlot.ts
│ └── unit.ts
├── serialize.ts
├── signer.ts
└── utils.ts
├── tsconfig.json
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/env", { "targets": { "node": "12" } }],
4 | ["@babel/typescript"]
5 | ],
6 | "plugins": [
7 | "@babel/plugin-transform-runtime",
8 | "@babel/proposal-object-rest-spread",
9 | ["@babel/plugin-proposal-decorators", { "legacy": true }],
10 | ["@babel/proposal-class-properties", { "loose": true }],
11 | "babel-plugin-transform-typescript-metadata",
12 | ["@babel/plugin-transform-private-property-in-object", { "loose": true }],
13 | ["@babel/plugin-transform-private-methods", { "loose": true }]
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/.github/workflows/deploy_docs.yml:
--------------------------------------------------------------------------------
1 | name: Deploy docs to GitHub Pages
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | deploy:
10 | runs-on: ubuntu-latest
11 |
12 | strategy:
13 | matrix:
14 | node-version: [19.x]
15 |
16 | steps:
17 | - uses: actions/checkout@v4
18 | - name: Use Node.js ${{ matrix.node-version }}
19 | uses: actions/setup-node@v4
20 | with:
21 | node-version: ${{ matrix.node-version }}
22 | - name: Install dependencies
23 | run: yarn install
24 | working-directory: ./docs
25 | - name: Build
26 | run: yarn build
27 | working-directory: ./docs
28 | - name: Deploy to GitHub Pages
29 | uses: JamesIves/github-pages-deploy-action@v4.6.0
30 | with:
31 | branch: gh-pages
32 | folder: ./docs/build
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish to NPM
2 | on:
3 | release:
4 | types: [published]
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 | strategy:
9 | matrix:
10 | node-version: [18.x]
11 | steps:
12 | - uses: actions/checkout@v4
13 | with:
14 | fetch-depth: 0
15 | token: ${{ secrets.AVORTYBOT_TOKEN }}
16 | - name: Setup Node
17 | uses: actions/setup-node@v4
18 | with:
19 | node-version: ${{ matrix.node-version }}
20 | registry-url: 'https://registry.npmjs.org'
21 | - name: Install dependencies and build
22 | run: yarn && yarn prepublish
23 | - name: Extract tag version
24 | id: extract_tag
25 | run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/v}
26 | - name: Update version in package.json
27 | run: |
28 | npm --no-git-tag-version version ${{ steps.extract_tag.outputs.VERSION }}
29 | git config --global user.email "contact.avorty@gmail.com"
30 | git config --global user.name "AvortyBot"
31 | git commit -am "chore: bump version to ${{ steps.extract_tag.outputs.VERSION }}"
32 | git push origin HEAD:main
33 | - name: Publish to npm
34 | run: npm publish --access public
35 | env:
36 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
37 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 | on:
3 | pull_request:
4 | push:
5 | branches: ["main"]
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 | strategy:
11 | matrix:
12 | node-version: [18.x]
13 | steps:
14 | - uses: actions/checkout@v4
15 | - name: Setup Node
16 | uses: actions/setup-node@v4
17 | with:
18 | node-version: ${{ matrix.node-version }}
19 | - name: Install dependencies and run checks
20 | run: yarn
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # Next.js build output
79 | .next
80 |
81 | # Nuxt.js build / generate output
82 | .nuxt
83 | dist
84 |
85 | # Gatsby files
86 | .cache/
87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
88 | # https://nextjs.org/blog/next-9-1#public-directory-support
89 | # public
90 |
91 | # vuepress build output
92 | .vuepress/dist
93 |
94 | # Serverless directories
95 | .serverless/
96 |
97 | # FuseBox cache
98 | .fusebox/
99 |
100 | # DynamoDB Local files
101 | .dynamodb/
102 |
103 | # TernJS port file
104 | .tern-port
105 |
106 | # Babel output
107 | lib/
108 |
109 | #Auto-gen files for development
110 | package-lock.json
111 | yarn.lock
112 |
113 | # File for testing in development
114 | dev.js
115 | dev.ts
116 | account.json
117 | keystore.json
118 |
119 | # Other package managers
120 | pnpm-lock.yaml
121 |
122 | # IDE's
123 | .idea/
124 | .vscode/
125 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .github/
2 | /docs/
3 | /src/
4 | /test/
5 | .env
6 | dev.js
7 | tsconfig.json
8 | /assets
9 | keystore.json
10 | account.json
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Capure
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |

2 |
3 | # Vulcan Api JS
4 |
5 | Unoffical Vulcan UONET+ SDK for JavaScript / TypeScript
6 |
7 | [](https://www.npmjs.com/package/vulcan-api-js)
8 |
9 | ## Description
10 |
11 | This project is loosely based on [Vulcan API](https://github.com/kapi2289/vulcan-api).
12 |
13 | ## Docs
14 |
15 | You can find the docs [here](https://vulcan-api.github.io/vulcan-api-js)
16 |
17 | ## License
18 |
19 | [MIT](https://github.com/Capure/vulcan-api-js/blob/master/LICENSE)
20 |
--------------------------------------------------------------------------------
/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
29 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependencies
2 | /node_modules
3 |
4 | # Production
5 | /build
6 |
7 | # Generated files
8 | .docusaurus
9 | .cache-loader
10 |
11 | # Misc
12 | .DS_Store
13 | .env.local
14 | .env.development.local
15 | .env.test.local
16 | .env.production.local
17 |
18 | npm-debug.log*
19 | yarn-debug.log*
20 | yarn-error.log*
21 |
22 | # Other package managers
23 | pnpm-lock.yaml
24 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Website
2 |
3 | This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.
4 |
5 | ### Installation
6 |
7 | ```
8 | $ yarn
9 | ```
10 |
11 | ### Local Development
12 |
13 | ```
14 | $ yarn start
15 | ```
16 |
17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
18 |
19 | ### Build
20 |
21 | ```
22 | $ yarn build
23 | ```
24 |
25 | This command generates static content into the `build` directory and can be served using any static contents hosting service.
26 |
27 | ### Deployment
28 |
29 | Using SSH:
30 |
31 | ```
32 | $ USE_SSH=true yarn deploy
33 | ```
34 |
35 | Not using SSH:
36 |
37 | ```
38 | $ GIT_USER= yarn deploy
39 | ```
40 |
41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
42 |
--------------------------------------------------------------------------------
/docs/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
3 | };
4 |
--------------------------------------------------------------------------------
/docs/docs/contributing/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 | "label": "Contributing",
3 | "position": 5,
4 | "link": {
5 | "type": "generated-index",
6 | "description": "Some details on how to contribute to the project."
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/docs/docs/contributing/docs.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 3
3 | ---
4 |
5 | # Contributing to the docs
6 |
7 | On every page you can find a link to the source file of the page. You can edit the file and create a pull request.
8 |
9 | ## Docusaurus
10 |
11 | Our documentation uses Docusaurus. If you want to learn about Markdown or special features of Docusaurus you can look at the [Docusaurus documentation](https://docusaurus.io/docs).
--------------------------------------------------------------------------------
/docs/docs/contributing/general.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | ---
4 |
5 | # General information
6 |
7 | Contributions are welcome! We are happy to accept contributions in the form of pull requests, issues, or just general feedback and discussion.
8 |
9 | ## Contributing to the SDK
10 |
11 | See [Contributing to the SDK](./sdk).
12 |
13 | ## Contributing to the documentation
14 |
15 | See [Contributing to the documentation](./docs).
16 |
17 | If you have any questions feel free to contact us.
--------------------------------------------------------------------------------
/docs/docs/contributing/sdk.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | ---
4 |
5 | # Contributing to the SDK
6 |
7 | You can look on the [list of issues](https://github.com/avorty/vulcan-api-js/issues) and pick one to work on. If you have an idea for a new feature, you can create an issue and discuss it with us.
8 |
9 | ## Running SDK in the development mode
10 |
11 | We use yarn as a package manager.
12 |
13 | 1. First install the dependencies
14 |
15 | ```bash
16 | yarn install
17 | ```
18 |
19 | 2. Then run the SDK in development mode
20 |
21 | ```bash
22 | yarn dev
23 | ```
--------------------------------------------------------------------------------
/docs/docs/example_projects.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 4
3 | ---
4 |
5 | # Example projects
6 |
7 | Here is a list of example projects that use our SDK.
8 |
9 | ## BasedBook
10 |
11 | BasedBook is a web application created by [Avorty](https://github.com/avorty) and it's a great example of how to use our SDK with Nest.js. See [backend repository](https://github.com/avorty/basedbook-backend).
--------------------------------------------------------------------------------
/docs/docs/getting-started/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 | "label": "Getting started",
3 | "position": 1,
4 | "link": {
5 | "type": "generated-index",
6 | "description": "How to get started with the SDK"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/docs/docs/getting-started/installation.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | ---
4 |
5 | # Installation
6 |
7 | NPM
8 | ```bash
9 | npm install vulcan-api-js
10 | ```
11 |
12 | YARN
13 | ```bash
14 | yarn add vulcan-api-js
15 | ```
16 |
17 | PNPM
18 | ```bash
19 | pnpm add vulcan-api-js
20 | ```
21 |
22 | :::info
23 | TypeScript typings are included.
24 | :::
25 |
26 | :::info
27 | React Native is now supported, but you must install the react-native-get-random-values npm module and import it before the vulcan-api-js module.
28 | :::
29 |
--------------------------------------------------------------------------------
/docs/docs/getting-started/usage.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 5
3 | ---
4 |
5 | # Usage
6 |
7 | First, you need to create a keystore, this is where your certificate is stored.
8 |
9 | You will have to save it somewhere as that's what gets registered in Vulcan.
10 |
11 | ```js
12 | const {Keystore} = require('vulcan-api-js');
13 | const fs = require('fs');
14 |
15 | const main = async () => {
16 | const keystore = new Keystore();
17 | await keystore.init();
18 |
19 | fs.writeFileSync("keystore.json", keystore.dumpToJsonString(), { encoding: 'utf-8' });
20 | };
21 |
22 | main();
23 | ```
24 |
25 | Then you will have to register the account.
26 |
27 | ```js
28 | const {Keystore, AccountTools, registerAccount} = require('vulcan-api-js');
29 | const fs = require('fs');
30 |
31 | const main = async () => {
32 | const keystore = new Keystore();
33 | keystore.loadFromJsonString(fs.readFileSync("keystore.json", { encoding: 'utf-8' }));
34 |
35 | const account = await registerAccount(keystore, {TOKEN}, {SYMBOL}, {PIN});
36 | fs.writeFileSync("account.json", AccountTools.dumpToJsonString(account), { encoding: 'utf-8' });
37 | };
38 |
39 | main();
40 | ```
41 |
42 | When you have your keystore and account generated, you can load them and use the SDK.
43 |
44 | ```js
45 | const {Keystore, AccountTools, VulcanHebe} = require('vulcan-api-js');
46 |
47 | const main = async () => {
48 | const keystore = new Keystore();
49 | keystore.loadFromJsonString(fs.readFileSync("keystore.json", { encoding: 'utf-8' }));
50 |
51 | const client = new VulcanHebe(keystore, AccountTools.loadFromJsonString(fs.readFileSync("account.json", { encoding: 'utf-8' })));
52 |
53 | // Pick your student (defaults to the first one)
54 | await client.selectStudent();
55 |
56 | // You can use the SDK here
57 | };
58 |
59 | main();
60 | ```
61 |
62 | :::info
63 | All the methods in the Vulcan API JS are async.
64 | :::
--------------------------------------------------------------------------------
/docs/docs/methods/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 | "label": "Methods",
3 | "position": 2,
4 | "link": {
5 | "type": "generated-index",
6 | "description": "All methods"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/docs/docs/methods/get_attendance.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 9
3 | ---
4 |
5 | # getAttendance
6 |
7 | Gets attendace from a date range.
8 |
9 | ## Arguments
10 |
11 | - (from) - [DateTime](../models/date_time)
12 | - (to) - [DateTime](../models/date_time)
13 |
14 | ## Returns
15 |
16 | This method will return an array of [Attendance](../models/attendance) objects with all attendance from the date range.
17 |
18 | ## Example
19 |
20 | ```js
21 | client.getAttendance({from, to}).then(attendance => {
22 | attendance.forEach(attendance => {
23 | // attendance
24 | })
25 | })
26 | ```
--------------------------------------------------------------------------------
/docs/docs/methods/get_exams.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 10
3 | ---
4 |
5 | # getExams
6 |
7 | Gets all exams since the last sync.
8 |
9 | ## Arguments
10 |
11 | - (lastSync?) - Date
12 |
13 | ## Returns
14 |
15 | This method will return an array of [Exam](../models/exam) objects with all exams since the last sync.
16 |
17 | ## Example
18 |
19 | ```js
20 | client.getExams({lastSync}).then(exams => {
21 | exams.forEach(exam => {
22 | // exams
23 | })
24 | })
25 | ```
--------------------------------------------------------------------------------
/docs/docs/methods/get_grades.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 5
3 | ---
4 |
5 | # getGrades
6 |
7 | Gets all grades since the last sync.
8 |
9 | ## Arguments
10 | - (lastSync?) - Date
11 |
12 | ## Returns
13 |
14 | This method will return an array of [Grade](../models/grade) objects with all grades since the last sync.
15 |
16 | ## Example
17 |
18 | ```js
19 | client.getGrades({lastSync}).then(grades => {
20 | grades.forEach(grade => {
21 | // grades
22 | })
23 | })
24 | ```
--------------------------------------------------------------------------------
/docs/docs/methods/get_homework.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 8
3 | ---
4 |
5 | # getHomework
6 |
7 | Gets all homework.
8 |
9 | ## Returns
10 |
11 | This method will return an array of [Homework](../models/homework) objects with all homework.
12 |
13 | ## Example
14 |
15 | ```js
16 | client.getHomework().then(homework => {
17 | homework.forEach(homework => {
18 | // homework
19 | })
20 | })
21 | ```
--------------------------------------------------------------------------------
/docs/docs/methods/get_lessons.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 3
3 | ---
4 |
5 | # getLessons
6 |
7 | Gets a list of lessons.
8 |
9 | ## Arguments
10 | - (dateFrom?) - Date
11 | - (dateTo?) - Date
12 |
13 | If the dates aren't provided defaults to today.
14 |
15 | ## Returns
16 |
17 | An array of [Lesson](../models/Lesson) objects
18 |
19 | ## Example
20 |
21 | ```js
22 | client.getLessons({dateFrom?}, {dateTo?}).then(lessons => {
23 | lessons.forEach(lesson => {
24 | // lessons
25 | })
26 | })
27 | ```
--------------------------------------------------------------------------------
/docs/docs/methods/get_lucky_number.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 4
3 | ---
4 |
5 | # getLuckyNumber
6 |
7 | Gets the lucky number for today.
8 |
9 | ## Returns
10 |
11 | An [LuckyNumber](../models/lucky_number) object.
12 |
13 | ## Example
14 |
15 | ```js
16 | client.getLuckyNumber().then(luckyNumber => {
17 | // luckyNumber
18 | })
19 | ```
--------------------------------------------------------------------------------
/docs/docs/methods/get_message_boxes.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 6
3 | ---
4 |
5 | # getMessageBoxes
6 |
7 | Gets all message boxes.
8 |
9 | ## Returns
10 |
11 | This method will return an array of [MessageBox](../models/message_box) objects with all message boxes.
12 |
13 | ## Example
14 |
15 | ```js
16 | client.getMessageBoxes().then(messageBoxes => {
17 | messageBoxes.forEach(messageBox => {
18 | // messageBoxes
19 | })
20 | })
21 | ```
--------------------------------------------------------------------------------
/docs/docs/methods/get_messages.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 7
3 | ---
4 |
5 | # getMessages
6 |
7 | Gets all messages.
8 |
9 | ## Arguments
10 | - (messageBox) - [MessageBox](../models/message_box)
11 |
12 | ## Returns
13 |
14 | This method will return an array of [Message](../models/message) objects with all messages in the message box.
15 |
16 | ## Example
17 |
18 | ```js
19 | client.getMessages({messageBox}).then(messages => {
20 | messages.forEach(message => {
21 | // messages
22 | })
23 | })
24 | ```
--------------------------------------------------------------------------------
/docs/docs/methods/get_students.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | ---
4 |
5 | # getStudents
6 |
7 | Gets a list of students registered in the account.
8 |
9 | ## Returns
10 | A list of [Student](../models/student) objects
11 |
12 | ## Example
13 |
14 | ```js
15 | client.getStudents().then(students => {
16 | students.forEach(student => {
17 | // students
18 | })
19 | })
20 | ```
--------------------------------------------------------------------------------
/docs/docs/methods/select_student.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | ---
4 |
5 | # selectStudent
6 |
7 | Sets the student that you want to use to access the API.
8 |
9 | ## Arguments
10 | - (student?) - [Student](../models/student)
11 |
12 | If a student is not provided the first one gets selected.
13 |
14 | ## Example
15 |
16 | ```js
17 | client.selectStudent({student?}).then(() => {
18 | // Do something
19 | })
20 | ```
--------------------------------------------------------------------------------
/docs/docs/models/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 | "label": "Models",
3 | "position": 3,
4 | "link": {
5 | "type": "generated-index",
6 | "description": "How to get started with the SDK"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/docs/docs/models/address.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 18
3 | ---
4 |
5 | # Address
6 |
7 | An object represents receiver of message.
8 |
9 | ## Properties
10 | - (globalKey) - string
11 | - (name) - string
12 | - (hasRead) - boolean
--------------------------------------------------------------------------------
/docs/docs/models/attendance.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 19
3 | ---
4 |
5 | # Attendance
6 |
7 | An object representing attendance details.
8 |
9 | ## Properties
10 | - (lessonId) - number
11 | - (id) - number
12 | - (lessonNumber) - number
13 | - (globalKey) - string
14 | - (lessonClassId) - number
15 | - (lessonClassGlobalKey) - string
16 | - (calculcatePresence) - boolean
17 | - (replacement) - boolean
18 | - (subject) - [Subject](./subject)
19 | - (topic) - string
20 | - (teacher) - [Teacher](./teacher)
21 | - (secondTeacher?) - [Teacher](./teacher)
22 | - (mainTeacher?) - [Teacher](./teacher)
23 | - (teamClass?) - [TeamClass](./team_class)
24 | - (classAlias?) - string
25 | - (date) - [DateTime](./date_time)
26 | - (time?) - [TimeSlot](./time_slot)
27 | - (dateModified?) - [DateTime](./date_time)
28 | - (auxPresenceId?) - number
29 | - (justificationStatus?) - string
30 | - (presenceType?) - [PresenceType](./presence_type)
31 | - (note?) - string
32 | - (publicResources?) - string
33 | - (remoteResources?) - string
34 | - (group?) - [TeamVirtual](./team_virtual)
35 | - (visible?) - boolean
36 |
--------------------------------------------------------------------------------
/docs/docs/models/date_time.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 6
3 | ---
4 |
5 | # DateTime
6 |
7 | An object representing date and time.
8 |
9 | ## Properties
10 | - (timestamp) - number
11 | - (date) - string
12 | - (time) - string
13 | - (dateDisplay?) - string
--------------------------------------------------------------------------------
/docs/docs/models/exam.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 20
3 | ---
4 |
5 | # Exam
6 |
7 | An object representing exam.
8 |
9 | ## Properties
10 | - (id) - number
11 | - (key) - string
12 | - (type) - string
13 | - (topic) - string
14 | - (dateCreated) - [DateTime](./date_time)
15 | - (dateModified) - [DateTime](./date_time)
16 | - (deadline) - [DateTime](./date_time)
17 | - (creator) - [Teacher](./teacher)
18 | - (subject) - [Subject](./subject)
19 | - (pupilId) - number
20 |
--------------------------------------------------------------------------------
/docs/docs/models/grade.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 14
3 | ---
4 |
5 | # Grade
6 |
7 | An object representing a grade.
8 |
9 | ## Properties
10 | - (id) - number
11 | - (key) - string
12 | - (pupilId) - number
13 | - (contentRaw) - string
14 | - (content) - string
15 | - (dateCreated) - [DateTime](./date_time)
16 | - (dateModify) - [DateTime](./date_time)
17 | - (creator) - [Teacher](./teacher)
18 | - (modifier) - [Teacher](./teacher)
19 | - (column) - [GradeColumn](./grade_column)
20 | - (value?) - number
21 | - (comment?) - string
22 | - (numerator?) - string
23 | - (denominator?) - number
--------------------------------------------------------------------------------
/docs/docs/models/grade_category.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 16
3 | ---
4 |
5 | # GradeCategory
6 |
7 | An object representing a grade category.
8 |
9 | ## Properties
10 | - (id) - number
11 | - (name) - string
12 | - (code) - string
13 |
--------------------------------------------------------------------------------
/docs/docs/models/grade_column.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 15
3 | ---
4 |
5 | # GradeColumn
6 |
7 | An object representing a type of grade.
8 |
9 | ## Properties
10 | - (id) - number
11 | - (key) - string
12 | - (periodId) - number
13 | - (name) - string
14 | - (code) - string
15 | - (group) - string
16 | - (number) - number
17 | - (weight) - number
18 | - (subject) - [Subject](./subject)
19 | - (category?) - [GradeCategory](./grade_category)
20 | - (period?) - [Period](./period)
--------------------------------------------------------------------------------
/docs/docs/models/homework.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 21
3 | ---
4 |
5 | # Homework
6 |
7 | An object representing homework.
8 |
9 | ## Properties
10 | - (id) - number
11 | - (key) - string
12 | - (homeworkId) - number
13 | - (content) - string
14 | - (dateCreated) - Date
15 | - (creator) - [Teacher](./teacher)
16 | - (subject) - [Subject](./subject)
17 | - (attachments) - Attachment[]
18 | - (isAnswerRequired) - boolean
19 | - (deadline) - Date
20 | - (answerDeadline) - Date
21 | - (answerDate) - Date
22 |
--------------------------------------------------------------------------------
/docs/docs/models/lesson.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 17
3 | ---
4 |
5 | # Lesson
6 |
7 | An object representing a lesson.
8 |
9 | ## Properties
10 | - (id?) - number
11 | - (date?) - [DateTime](./date_time)
12 | - (timeSlot?) - [TimeSlot](./time_slot)
13 | - (room?) - [Room](./room)
14 | - (teacherPrimary?) - [Teacher](./teacher)
15 | - (teacherSecondary?) - [Teacher](./teacher)
16 | - (subject?) - [Subject](./subject)
17 | - (event?) - string
18 | - (change?) - string
19 | - (class?) - [TeamClass](./team_class)
20 | - (pupilAlias?) - string
21 | - (distribution?) - [TeamVirtual](./team_virtual.md)
22 | - (visible?) - boolean
--------------------------------------------------------------------------------
/docs/docs/models/lesson_room.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 8
3 | ---
4 |
5 | # LessonRoom
6 |
7 | An object representing a classroom.
8 |
9 | ## Properties
10 | - (id) - number
11 | - (code) - string
--------------------------------------------------------------------------------
/docs/docs/models/lucky_number.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 13
3 | ---
4 |
5 | # LuckyNumber
6 |
7 | An object representing a lucky number.
8 |
9 | ## Properties
10 | - (day) - number
11 | - (number) - number
--------------------------------------------------------------------------------
/docs/docs/models/message.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 22
3 | ---
4 |
5 | # Message
6 |
7 | An object representing message.
8 |
9 | ## Properties
10 | - (id) - string
11 | - (globalKey) - string
12 | - (threadKey) - string
13 | - (subject) - string
14 | - (content) - string
15 | - (sentDate) - Date
16 | - (status) - number
17 | - (sender) - string
18 | - (receivers) - [Address](./address)[]
19 | - (attachments) - Attachment[]
20 | - (readDate?) - Date
21 |
--------------------------------------------------------------------------------
/docs/docs/models/message_box.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 23
3 | ---
4 |
5 | # MessageBox
6 |
7 | An object representing message box.
8 |
9 | ## Properties
10 | - (id) - string
11 | - (globalKey) - string
12 | - (name) - string
--------------------------------------------------------------------------------
/docs/docs/models/period.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 5
3 | ---
4 |
5 | # Period
6 |
7 | An object representing a school year period.
8 |
9 | ## Properties
10 | - (id) - number
11 | - (level) - number
12 | - (number) - number
13 | - (current) - boolean
14 | - (last) - boolean
15 | - (start) - any
16 | - (end) - any
--------------------------------------------------------------------------------
/docs/docs/models/presence_type.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 24
3 | ---
4 |
5 | # Presence type
6 |
7 | An object representing presence type.
8 |
9 | ## Properties
10 | - (id) - string
11 | - (name) - string
12 | - (symbol) - string
13 | - (category_id) - number
14 | - (category_name) - string
15 | - (position) - number
16 | - (presence) - boolean
17 | - (absence) - boolean
18 | - (exemption) - boolean
19 | - (late) - boolean
20 | - (justified) - boolean
21 | - (deleted) - boolean
--------------------------------------------------------------------------------
/docs/docs/models/pupil.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | ---
4 |
5 | # Pupil
6 |
7 | An object representing a person.
8 |
9 | ## Properties
10 | - (id) - number
11 | - (loginId) - number
12 | - (loginValue) - string
13 | - (firstName) - string
14 | - (secondName) - string
15 | - (surname) - string
16 | - (sex) - boolean:
17 |
18 | ### Sex
19 |
20 | - Male - true
21 | - Female - false
22 |
--------------------------------------------------------------------------------
/docs/docs/models/room.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 25
3 | ---
4 |
5 | # Room
6 |
7 | An object representing room.
8 |
9 | ## Properties
10 | - (id) - number
11 | - (code) - string
--------------------------------------------------------------------------------
/docs/docs/models/school.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 4
3 | ---
4 |
5 | # School
6 |
7 | An object representing a single school.
8 |
9 | ## Properties
10 | - (id) - number
11 | - (name) - string
12 | - (short) - string
13 | - (address) - string
--------------------------------------------------------------------------------
/docs/docs/models/student.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | ---
4 |
5 | # Student
6 |
7 | A student object returned by the SDK.
8 |
9 | ## Properties
10 | - (symbol) - string
11 | - (symbol_code) - string
12 | - (pupil) - [Pupil](./pupil)
13 | - (unit) - [Unit](./unit)
14 | - (school) - [School](./school)
15 | - (periods) - [Period](./period)[]
16 |
--------------------------------------------------------------------------------
/docs/docs/models/subject.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 10
3 | ---
4 |
5 | # Subject
6 |
7 | An object representing a school subject.
8 |
9 | ## Properties
10 | - (id) - number
11 | - (key) - string
12 | - (name) - string
13 | - (code) - string
14 | - (position) - number
--------------------------------------------------------------------------------
/docs/docs/models/teacher.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 9
3 | ---
4 |
5 | # Teacher
6 |
7 | An object representing a teacher.
8 |
9 | ## Properties
10 | - (id) - number
11 | - (name) - string
12 | - (surname) - string
13 | - (displayName) - string
--------------------------------------------------------------------------------
/docs/docs/models/team_class.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 11
3 | ---
4 |
5 | # Subject
6 |
7 | An object representing a school class.
8 |
9 | ## Properties
10 | - (id) - number
11 | - (key) - string
12 | - (displayName) - string
13 | - (symbol) - string
--------------------------------------------------------------------------------
/docs/docs/models/team_virtual.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 12
3 | ---
4 |
5 | # TeamVirtual
6 |
7 | An object representing a part of school class.
8 |
9 | ## Properties
10 | - (id) - number
11 | - (key) - string
12 | - (shortcut) - string
13 | - (name) - string
14 | - (partType) - string
--------------------------------------------------------------------------------
/docs/docs/models/time_slot.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 7
3 | ---
4 |
5 | # Period
6 |
7 | An object representing a lesson time.
8 |
9 | ## Properties
10 | - (id) - number
11 | - (start) - any
12 | - (end) - any
13 | - (display) - string
14 | - (position) - number
--------------------------------------------------------------------------------
/docs/docs/models/unit.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 3
3 | ---
4 |
5 | # Unit
6 |
7 | An object representing a school or a group of schools.
8 |
9 | ## Properties
10 | - (id) - number
11 | - (symbol) - string
12 | - (name) - string
13 | - (short) - string
14 | - (displayName) - string
15 | - (address) - string
16 | - (restUrl) - string
--------------------------------------------------------------------------------
/docs/docusaurus.config.ts:
--------------------------------------------------------------------------------
1 | import {themes as prismThemes} from 'prism-react-renderer';
2 | import type {Config} from '@docusaurus/types';
3 | import type * as Preset from '@docusaurus/preset-classic';
4 |
5 | const config: Config = {
6 | title: 'vulcan-api-js',
7 | tagline: 'Unoffical Vulcan UONET+ SDK for JavaScript / TypeScript',
8 | favicon: 'img/favicon.ico',
9 | url: 'https://avorty.github.io',
10 | baseUrl: '/vulcan-api-js/',
11 |
12 | // GitHub pages deployment config.
13 | // If you aren't using GitHub pages, you don't need these.
14 | organizationName: 'avorty', // Usually your GitHub org/user name.
15 | projectName: 'vulcan-api-js', // Usually your repo name.
16 |
17 | onBrokenLinks: 'throw',
18 | onBrokenMarkdownLinks: 'warn',
19 |
20 | // Even if you don't use internationalization, you can use this field to set
21 | // useful metadata like html lang. For example, if your site is Chinese, you
22 | // may want to replace "en" with "zh-Hans".
23 | i18n: {
24 | defaultLocale: 'en',
25 | locales: ['en'],
26 | },
27 | plugins: [require.resolve('docusaurus-lunr-search')],
28 | presets: [
29 | [
30 | 'classic',
31 | {
32 | docs: {
33 | sidebarPath: './sidebars.ts',
34 | // Please change this to your repo.
35 | // Remove this to remove the "edit this page" links.
36 | editUrl:
37 | 'https://github.com/avorty/vulcan-api-js/tree/main/docs/',
38 | },
39 | theme: {
40 | customCss: './src/css/custom.css',
41 | },
42 | } satisfies Preset.Options,
43 | ],
44 | ],
45 |
46 | themeConfig: {
47 | image: 'img/docusaurus-social-card.jpg',
48 | navbar: {
49 | title: 'vulcan-api-js',
50 | logo: {
51 | alt: 'Logo',
52 | src: 'img/logo.svg',
53 | },
54 | items: [
55 | {
56 | type: 'docSidebar',
57 | sidebarId: 'tutorialSidebar',
58 | position: 'left',
59 | label: 'Documentation',
60 | },
61 | {
62 | href: 'https://github.com/avorty/vulcan-api-js',
63 | label: 'GitHub',
64 | position: 'right',
65 | },
66 | ],
67 | },
68 | footer: {
69 | style: 'dark',
70 | copyright: `Copyright © ${new Date().getFullYear()} Avorty
Built with Docusaurus.`,
71 | },
72 | prism: {
73 | theme: prismThemes.github,
74 | darkTheme: prismThemes.dracula,
75 | },
76 | } satisfies Preset.ThemeConfig,
77 | };
78 |
79 | export default config;
80 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docs",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "docusaurus": "docusaurus",
7 | "start": "docusaurus start",
8 | "build": "docusaurus build",
9 | "swizzle": "docusaurus swizzle",
10 | "deploy": "docusaurus deploy",
11 | "clear": "docusaurus clear",
12 | "serve": "docusaurus serve",
13 | "write-translations": "docusaurus write-translations",
14 | "write-heading-ids": "docusaurus write-heading-ids",
15 | "typecheck": "tsc"
16 | },
17 | "dependencies": {
18 | "@docusaurus/core": "3.0.1",
19 | "@docusaurus/preset-classic": "3.0.1",
20 | "@mdx-js/react": "^3.0.0",
21 | "clsx": "^2.0.0",
22 | "docusaurus-lunr-search": "^3.3.1",
23 | "lunr": "^2.3.9",
24 | "prism-react-renderer": "^2.3.0",
25 | "react": "^18.0.0",
26 | "react-dom": "^18.0.0"
27 | },
28 | "devDependencies": {
29 | "@docusaurus/module-type-aliases": "3.0.1",
30 | "@docusaurus/tsconfig": "3.0.1",
31 | "@docusaurus/types": "3.0.1",
32 | "typescript": "~5.2.2"
33 | },
34 | "browserslist": {
35 | "production": [
36 | ">0.5%",
37 | "not dead",
38 | "not op_mini all"
39 | ],
40 | "development": [
41 | "last 3 chrome version",
42 | "last 3 firefox version",
43 | "last 5 safari version"
44 | ]
45 | },
46 | "engines": {
47 | "node": ">=18.0"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/docs/sidebars.ts:
--------------------------------------------------------------------------------
1 | import type {SidebarsConfig} from '@docusaurus/plugin-content-docs';
2 |
3 | /**
4 | * Creating a sidebar enables you to:
5 | - create an ordered group of docs
6 | - render a sidebar for each doc of that group
7 | - provide next/previous navigation
8 |
9 | The sidebars can be generated from the filesystem, or explicitly defined here.
10 |
11 | Create as many sidebars as you want.
12 | */
13 | const sidebars: SidebarsConfig = {
14 | // By default, Docusaurus generates a sidebar from the docs folder structure
15 | tutorialSidebar: [{type: 'autogenerated', dirName: '.'}],
16 |
17 | // But you can create a sidebar manually
18 | /*
19 | tutorialSidebar: [
20 | 'intro',
21 | 'hello',
22 | {
23 | type: 'category',
24 | label: 'Tutorial',
25 | items: ['tutorial-basics/create-a-document'],
26 | },
27 | ],
28 | */
29 | };
30 |
31 | export default sidebars;
32 |
--------------------------------------------------------------------------------
/docs/src/components/HomepageFeatures/index.tsx:
--------------------------------------------------------------------------------
1 | import clsx from 'clsx';
2 | import Heading from '@theme/Heading';
3 | import styles from './styles.module.css';
4 |
5 | type FeatureItem = {
6 | title: string;
7 | Svg: React.ComponentType>;
8 | description: JSX.Element;
9 | };
10 |
11 | const FeatureList: FeatureItem[] = [
12 | {
13 | title: 'Easy to Use',
14 | Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default,
15 | description: (
16 | <>
17 | Our API is simple and easy to use. You can get started in minutes.
18 | >
19 | ),
20 | },
21 | {
22 | title: 'Clean and simple documentation',
23 | Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default,
24 | description: (
25 | <>
26 | Our documentation is clean and simple. We describe every methods and properties available in our SDK in detail.
27 | >
28 | ),
29 | },
30 | {
31 | title: 'TypeScript support',
32 | Svg: require('@site/static/img/undraw_docusaurus_react.svg').default,
33 | description: (
34 | <>
35 | We support TypeScript out of the box. No need to install any additional packages.
36 | >
37 | ),
38 | },
39 | ];
40 |
41 | function Feature({ title, Svg, description }: FeatureItem) {
42 | return (
43 |
44 |
45 |
46 |
47 |
48 |
{title}
49 |
{description}
50 |
51 |
52 | );
53 | }
54 |
55 | export default function HomepageFeatures(): JSX.Element {
56 | return (
57 |
58 |
59 |
60 | {FeatureList.map((props, idx) => (
61 |
62 | ))}
63 |
64 |
65 |
66 | );
67 | }
68 |
--------------------------------------------------------------------------------
/docs/src/components/HomepageFeatures/styles.module.css:
--------------------------------------------------------------------------------
1 | .features {
2 | display: flex;
3 | align-items: center;
4 | padding: 2rem 0;
5 | width: 100%;
6 | }
7 |
8 | .featureSvg {
9 | height: 200px;
10 | width: 200px;
11 | }
12 |
--------------------------------------------------------------------------------
/docs/src/css/custom.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Any CSS included here will be global. The classic template
3 | * bundles Infima by default. Infima is a CSS framework designed to
4 | * work well for content-centric websites.
5 | */
6 |
7 | /* You can override the default Infima variables here. */
8 | :root {
9 | --ifm-color-primary: #2e8555;
10 | --ifm-color-primary-dark: #29784c;
11 | --ifm-color-primary-darker: #277148;
12 | --ifm-color-primary-darkest: #205d3b;
13 | --ifm-color-primary-light: #33925d;
14 | --ifm-color-primary-lighter: #359962;
15 | --ifm-color-primary-lightest: #3cad6e;
16 | --ifm-code-font-size: 95%;
17 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
18 | }
19 |
20 | /* For readability concerns, you should choose a lighter palette in dark mode. */
21 | [data-theme='dark'] {
22 | --ifm-color-primary: #25c2a0;
23 | --ifm-color-primary-dark: #21af90;
24 | --ifm-color-primary-darker: #1fa588;
25 | --ifm-color-primary-darkest: #1a8870;
26 | --ifm-color-primary-light: #29d5b0;
27 | --ifm-color-primary-lighter: #32d8b4;
28 | --ifm-color-primary-lightest: #4fddbf;
29 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
30 | }
31 |
--------------------------------------------------------------------------------
/docs/src/pages/index.module.css:
--------------------------------------------------------------------------------
1 | /**
2 | * CSS files with the .module.css suffix will be treated as CSS modules
3 | * and scoped locally.
4 | */
5 |
6 | .heroBanner {
7 | padding: 4rem 0;
8 | text-align: center;
9 | position: relative;
10 | overflow: hidden;
11 | }
12 |
13 | @media screen and (max-width: 996px) {
14 | .heroBanner {
15 | padding: 2rem;
16 | }
17 | }
18 |
19 | .buttons {
20 | display: flex;
21 | align-items: center;
22 | justify-content: center;
23 | }
24 |
--------------------------------------------------------------------------------
/docs/src/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import clsx from 'clsx';
2 | import Link from '@docusaurus/Link';
3 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
4 | import Layout from '@theme/Layout';
5 | import HomepageFeatures from '@site/src/components/HomepageFeatures';
6 | import Heading from '@theme/Heading';
7 |
8 | import styles from './index.module.css';
9 |
10 | function HomepageHeader() {
11 | const { siteConfig } = useDocusaurusContext();
12 | return (
13 |
28 | );
29 | }
30 |
31 | export default function Home(): JSX.Element {
32 | const { siteConfig } = useDocusaurusContext();
33 | return (
34 |
37 |
38 |
39 |
40 |
41 |
42 | );
43 | }
44 |
--------------------------------------------------------------------------------
/docs/src/pages/markdown-page.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Markdown page example
3 | ---
4 |
5 | # Markdown page example
6 |
7 | You don't need React to write simple standalone pages.
8 |
--------------------------------------------------------------------------------
/docs/static/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vulcan-api/vulcan-api-js/c6659a29ead7c6adf92ccc7138d2a3fa7e455add/docs/static/.nojekyll
--------------------------------------------------------------------------------
/docs/static/img/docusaurus-social-card.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vulcan-api/vulcan-api-js/c6659a29ead7c6adf92ccc7138d2a3fa7e455add/docs/static/img/docusaurus-social-card.jpg
--------------------------------------------------------------------------------
/docs/static/img/docusaurus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vulcan-api/vulcan-api-js/c6659a29ead7c6adf92ccc7138d2a3fa7e455add/docs/static/img/docusaurus.png
--------------------------------------------------------------------------------
/docs/static/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vulcan-api/vulcan-api-js/c6659a29ead7c6adf92ccc7138d2a3fa7e455add/docs/static/img/favicon.ico
--------------------------------------------------------------------------------
/docs/static/img/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/static/img/undraw_docusaurus_mountain.svg:
--------------------------------------------------------------------------------
1 |
172 |
--------------------------------------------------------------------------------
/docs/static/img/undraw_docusaurus_react.svg:
--------------------------------------------------------------------------------
1 |
171 |
--------------------------------------------------------------------------------
/docs/static/img/undraw_docusaurus_tree.svg:
--------------------------------------------------------------------------------
1 |
41 |
--------------------------------------------------------------------------------
/docs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | // This file is not used in compilation. It is here just for a nice editor experience.
3 | "extends": "@docusaurus/tsconfig",
4 | "compilerOptions": {
5 | "baseUrl": "."
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "lib/index.js",
3 | "name": "vulcan-api-js",
4 | "version": "3.5.4",
5 | "description": "Unofficial API for UONET+ e-register from Vulcan.",
6 | "scripts": {
7 | "prepublish": "rollup -c rollup.config.mjs & yarn tsc",
8 | "type-check": "tsc"
9 | },
10 | "repository": "https://github.com/avorty/vulcan-api-js.git",
11 | "homepage": "https://avorty.github.io/vulcan-api-js",
12 | "author": "Avorty",
13 | "license": "MIT",
14 | "type": "module",
15 | "exports": {
16 | "require": "./lib/index.cjs",
17 | "import": "./lib/index.js"
18 | },
19 | "dependencies": {
20 | "@babel/runtime": "^7.23.6",
21 | "axios": "^1.6.2",
22 | "buffer": "^6.0.3",
23 | "cross-fetch": "^4.0.0",
24 | "moment": "^2.29.4",
25 | "node-forge": "^1.3.1",
26 | "querystring": "^0.2.1",
27 | "reflect-metadata": "^0.2.1",
28 | "uuid": "^9.0.1"
29 | },
30 | "devDependencies": {
31 | "@babel/cli": "^7.23.4",
32 | "@babel/core": "^7.23.6",
33 | "@babel/plugin-proposal-class-properties": "^7.18.6",
34 | "@babel/plugin-proposal-decorators": "^7.23.6",
35 | "@babel/plugin-proposal-object-rest-spread": "^7.20.7",
36 | "@babel/plugin-transform-runtime": "^7.23.6",
37 | "@babel/preset-env": "^7.23.6",
38 | "@babel/preset-typescript": "^7.23.3",
39 | "@rollup/plugin-babel": "^6.0.4",
40 | "@rollup/plugin-commonjs": "^25.0.7",
41 | "@rollup/plugin-json": "^6.1.0",
42 | "@rollup/plugin-node-resolve": "^15.2.3",
43 | "@types/dateformat": "^5.0.2",
44 | "@types/node": "^20.10.5",
45 | "@types/node-fetch": "^2.6.9",
46 | "@types/node-forge": "^1.3.10",
47 | "@types/uuid": "^9.0.7",
48 | "babel-plugin-transform-typescript-metadata": "^0.3.2",
49 | "dotenv": "^16.3.1",
50 | "rollup": "^4.9.1",
51 | "typescript": "^5.3.3",
52 | "yarn": "^1.22.21"
53 | },
54 | "optionalDependencies": {
55 | "node-webcrypto-ossl": "^1.0.49"
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/rollup.config.mjs:
--------------------------------------------------------------------------------
1 | import commonjs from '@rollup/plugin-commonjs';
2 | import resolve from '@rollup/plugin-node-resolve';
3 | import babel from '@rollup/plugin-babel';
4 | import json from '@rollup/plugin-json';
5 | import fs from 'fs';
6 |
7 | const extensions = [
8 | '.js', '.jsx', '.ts', '.tsx',
9 | ];
10 |
11 | const packageRaw = fs.readFileSync("./package.json");
12 | const pkg = JSON.parse(packageRaw);
13 |
14 | const name = 'RollupTypeScriptBabel';
15 |
16 | export default {
17 | input: './src/index.ts',
18 |
19 | // Specify here external modules which you don't want to include in your bundle (for instance: 'lodash', 'moment' etc.)
20 | // https://rollupjs.org/guide/en#external-e-external
21 | external: [...Object.keys(pkg.dependencies || {})],
22 |
23 | plugins: [
24 | // Allows node_modules resolution
25 | resolve({ extensions }),
26 |
27 | // Allow bundling cjs modules. Rollup doesn't understand cjs
28 | commonjs(),
29 |
30 | json(),
31 | // Compile TypeScript/JavaScript files
32 | babel({ extensions, include: ['src/**/*'], babelHelpers: 'runtime' }),
33 | ],
34 |
35 | output: [
36 | {
37 | file: pkg.exports.import,
38 | format: 'es',
39 | },
40 | {
41 | file: pkg.exports.require,
42 | format: 'cjs',
43 | },
44 | ],
45 | };
46 |
--------------------------------------------------------------------------------
/src/api.ts:
--------------------------------------------------------------------------------
1 | import { Keystore } from "./keystore";
2 | import "cross-fetch/polyfill";
3 | import { getSignatureValues } from "./signer";
4 | import { ApiHelper } from "./apiHelper";
5 | import {
6 | APP_NAME,
7 | APP_OS,
8 | APP_USER_AGENT,
9 | APP_VERSION,
10 | millis,
11 | nowIso,
12 | uuid,
13 | } from "./utils";
14 | import { Student, Account } from "./models";
15 |
16 | export class Api {
17 | private keystore: Keystore;
18 | public account?: any;
19 | private restUrl?: any;
20 | public student?: any;
21 | public period?: any;
22 | public helper: ApiHelper;
23 |
24 | constructor(keystore: Keystore, account?: Account) {
25 | this.keystore = keystore;
26 | if (account) {
27 | this.account = account;
28 | this.restUrl = account.restUrl;
29 | }
30 | this.helper = new ApiHelper(this);
31 | }
32 |
33 | private buildPayload = (envelope: any) => ({
34 | AppName: APP_NAME,
35 | AppVersion: APP_VERSION,
36 | CertificateId: this.keystore.fingerprint,
37 | Envelope: envelope,
38 | FirebaseToken: this.keystore.firebaseToken,
39 | API: 1,
40 | RequestId: uuid(),
41 | Timestamp: millis(),
42 | TimestampFormatted: nowIso(),
43 | });
44 |
45 | private buildHeaders = (fullUrl: string, payload: string) => {
46 | if (!this.keystore.fingerprint || !this.keystore.privateKey) {
47 | throw Error("Keystore is not initialized!");
48 | }
49 | const dt = new Date();
50 | const { digest, canonicalUrl, signature } = getSignatureValues(
51 | this.keystore.fingerprint,
52 | this.keystore.privateKey,
53 | payload,
54 | fullUrl,
55 | dt.toUTCString()
56 | );
57 |
58 | const headers: any = {
59 | "User-Agent": APP_USER_AGENT,
60 | vOS: APP_OS,
61 | vDeviceModel: this.keystore.deviceModel,
62 | vAPI: "1",
63 | vDate: dt.toUTCString(),
64 | vCanonicalUrl: canonicalUrl,
65 | Signature: signature,
66 | };
67 |
68 | if (digest) {
69 | headers["Digest"] = digest;
70 | headers["Content-Type"] = "application/json";
71 | }
72 |
73 | return headers;
74 | };
75 |
76 | private request = async (method: "GET" | "POST", url: string, body?: any) => {
77 | const fullUrl = url.startsWith("http")
78 | ? url
79 | : this.restUrl
80 | ? this.restUrl + url
81 | : undefined;
82 | if (!fullUrl) {
83 | throw Error("Relative URL specified but no account loaded!");
84 | }
85 | const payload =
86 | body && method === "POST"
87 | ? JSON.stringify(this.buildPayload(body))
88 | : null;
89 | const headers = this.buildHeaders(fullUrl, payload === null ? "" : payload);
90 | const options: any = {
91 | headers: headers,
92 | method: method,
93 | };
94 | if (payload !== null) {
95 | options["body"] = payload;
96 | }
97 | try {
98 | const rawRes = await fetch(fullUrl, options);
99 | const jsonRes: any = await rawRes.json();
100 | const status = jsonRes["Status"];
101 | const envelope = jsonRes["Envelope"];
102 | if (status.Code !== 0) {
103 | throw Error(status["Message"]);
104 | }
105 | return envelope;
106 | } catch (e) {
107 | throw e;
108 | }
109 | };
110 |
111 | public get = async (url: string, inQuery?: any) => {
112 | const query = inQuery
113 | ? `${(() => {
114 | let queryToReturn = "";
115 | for (let item in inQuery) {
116 | queryToReturn += `&${item}=${encodeURIComponent(inQuery[item])}`;
117 | }
118 | return queryToReturn.substr(1);
119 | })()}`
120 | : undefined;
121 |
122 | if (query) {
123 | url += `?${query}`;
124 | }
125 |
126 | return await this.request("GET", url);
127 | };
128 |
129 | public post = async (url: string, body: any) =>
130 | await this.request("POST", url, body);
131 |
132 | public setStudent(student: Student) {
133 | this.student = student;
134 | this.restUrl = `${this.restUrl}${student.unit.symbol}/`;
135 | this.period = student.periods.filter((item) => item.current)[0];
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/apiHelper.ts:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 | import { Api } from "./api";
3 | import {
4 | DATA_BY_MESSAGEBOX,
5 | DATA_BY_PERIOD,
6 | DATA_BY_PERSON,
7 | DATA_BY_PUPIL,
8 | DATA_ROOT
9 | } from "./endpoints";
10 | import { Account } from "./models";
11 | import { Period } from "./models";
12 | import { Student } from "./models";
13 |
14 | export enum FilterType {
15 | BY_PUPIL = 0,
16 | BY_PERSON = 1,
17 | BY_PERIOD = 2,
18 | BY_MESSAGEBOX = 3,
19 | }
20 |
21 | export const getEndpoint = (type: FilterType) => {
22 | switch (type) {
23 | case FilterType.BY_PUPIL:
24 | return DATA_BY_PUPIL;
25 | case FilterType.BY_PERSON:
26 | return DATA_BY_PERSON;
27 | case FilterType.BY_PERIOD:
28 | return DATA_BY_PERIOD;
29 | case FilterType.BY_MESSAGEBOX:
30 | return DATA_BY_MESSAGEBOX;
31 | default:
32 | return null;
33 | }
34 | };
35 |
36 | export class ApiHelper {
37 | private api: Api;
38 |
39 | constructor(api: Api) {
40 | this.api = api;
41 | }
42 |
43 | public async getList(
44 | endpoint: string,
45 | deleted: boolean = false,
46 | lastSync?: Date,
47 | dateFrom?: Date,
48 | dateTo?: Date,
49 | filterType?: FilterType,
50 | messageBox?: string,
51 | folder?: number,
52 | params?: any
53 | ) {
54 | let url = "";
55 | if (!this.api) {
56 | throw Error("You must select a student!");
57 | }
58 | if (deleted) {
59 | throw Error("Getting deleted data IDs is not implemented yet.");
60 | }
61 | if (filterType !== undefined) {
62 | url = `${DATA_ROOT}/${endpoint}/${getEndpoint(filterType)}`;
63 | } else {
64 | url = `${DATA_ROOT}/${endpoint}`;
65 | }
66 | let query: any = {};
67 | const account: Account = this.api.account;
68 | const student: Student = this.api.student;
69 | const period: Period = this.api.period;
70 | switch (filterType) {
71 | case FilterType.BY_PUPIL:
72 | query["unitId"] = student.unit.id;
73 | query["pupilId"] = student.pupil.id;
74 | query["periodId"] = period.id;
75 | break;
76 | case FilterType.BY_PERSON:
77 | query["loginId"] = account.loginId;
78 | break;
79 | case FilterType.BY_PERIOD:
80 | query["periodId"] = period.id;
81 | query["pupilId"] = student.pupil.id;
82 | break;
83 | case FilterType.BY_MESSAGEBOX:
84 | if(!messageBox)
85 | throw Error('No messageBox specified!');
86 | query['box'] = messageBox;
87 | break;
88 | default:
89 | break;
90 | }
91 | if (dateFrom) {
92 | query["dateFrom"] = moment(dateFrom).format("YYYY-MM-DD");
93 | }
94 | if (dateTo) {
95 | query["dateTo"] = moment(dateTo).format("YYYY-MM-DD");
96 | }
97 | if (folder) {
98 | query['folder'] = folder;
99 | }
100 | query["lastId"] = "-2147483648"; // Comment from vulcan-api for python: don't ask, it's just Vulcan
101 | query["pageSize"] = 500;
102 | query["lastSyncDate"] = moment(
103 | lastSync || new Date("1970"),
104 | ).format("yyyy-mm-dd HH:MM:ss");
105 |
106 | if (params) {
107 | query = { ...query, ...params };
108 | }
109 |
110 | return await this.api.get(url, query);
111 | }
112 |
113 | public async getData(endpoint: string, query?: any) {
114 | const url = `${DATA_ROOT}/${endpoint}`;
115 | return await this.api.get(url, query);
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/endpoints.ts:
--------------------------------------------------------------------------------
1 | export const DEVICE_REGISTER = "api/mobile/register/new";
2 | export const STUDENT_LIST = "api/mobile/register/hebe";
3 |
4 | export const DATA_ROOT = "api/mobile";
5 | export const DATA_BY_PUPIL = "byPupil";
6 | export const DATA_BY_PERSON = "byPerson";
7 | export const DATA_BY_PERIOD = "byPeriod";
8 | export const DATA_BY_MESSAGEBOX = "byBox";
9 | export const DATA_DELETED = "deleted";
10 |
11 | export const DATA_INTERNAL_TIME = "internal/time";
12 | export const DATA_LUCKY_NUMBER = "school/lucky";
13 |
14 | export const DATA_EXAM = "exam";
15 | export const DATA_GRADE = "grade";
16 | export const DATA_GRADE_SUMMARY = "grade/summary";
17 | export const DATA_GRADE_AVERAGE = "grade/average";
18 | export const DATA_HOMEWORK = "homework";
19 | export const DATA_TIMETABLE = "schedule";
20 | export const DATA_TIMETABLE_CHANGES = "schedule/changes";
21 |
22 | export const DATA_MESSAGEBOX = "messagebox"
23 | export const DATA_MESSAGE = "messagebox/message"
24 | export const DATA_ATTENDANCE = "lesson";
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { Api } from "./api";
2 | import {
3 | DATA_ATTENDANCE, DATA_EXAM,
4 | DATA_GRADE,
5 | DATA_HOMEWORK,
6 | DATA_LUCKY_NUMBER,
7 | DATA_MESSAGE,
8 | DATA_MESSAGEBOX,
9 | DATA_TIMETABLE,
10 | DATA_TIMETABLE_CHANGES,
11 | DEVICE_REGISTER,
12 | STUDENT_LIST
13 | } from "./endpoints";
14 | import { Keystore } from "./keystore";
15 | import { APP_OS, getBaseUrl, uuid } from "./utils";
16 | import { FilterType } from "./apiHelper";
17 | import dateFormat from "dateformat";
18 | import moment from "moment";
19 | import { Account, ChangedLesson, Grade, Lesson, LuckyNumber, Student, MessageBox, Message, Homework, Attendance, Exam } from "./models";
20 |
21 | export { AccountTools } from "./utils";
22 | export * from "./keystore";
23 | export * from "./models";
24 |
25 | export const registerAccount = async (
26 | keystore: Keystore,
27 | token: string,
28 | symbol: string,
29 | pin: string
30 | ): Promise => {
31 | token = token.toUpperCase();
32 | symbol = symbol.toLowerCase();
33 |
34 | const body = {
35 | OS: APP_OS,
36 | DeviceModel: keystore.deviceModel,
37 | Certificate: keystore.certificate,
38 | CertificateType: "X509",
39 | CertificateThumbprint: keystore.fingerprint,
40 | PIN: pin,
41 | SecurityToken: token,
42 | SelfIdentifier: uuid(keystore.fingerprint),
43 | };
44 |
45 | const baseUrl = await getBaseUrl(token);
46 | const fullUrl = [baseUrl, symbol, DEVICE_REGISTER].join("/");
47 | const api = new Api(keystore);
48 | const response = await api.post(fullUrl, body);
49 | if (!response.RestUrl) {
50 | response.RestUrl = `${await getBaseUrl(token)}/${symbol}/`;
51 | }
52 |
53 | return new Account().serialize(response);
54 | };
55 |
56 | export class VulcanHebe {
57 | private readonly account: Account;
58 | private readonly api: Api;
59 | private student?: Student;
60 | constructor(keystore: Keystore, account: Account) {
61 | this.account = account;
62 | this.api = new Api(keystore, this.account);
63 | }
64 |
65 | public async getStudents() {
66 | const data: any = await this.api.get(STUDENT_LIST);
67 | const studentsToReturn: Student[] = data.map(
68 | (item: any): Student => new Student().serialize(item)
69 | );
70 | return studentsToReturn;
71 | }
72 |
73 | public async selectStudent(studentToSet?: Student) {
74 | if (studentToSet) {
75 | this.student = studentToSet;
76 | this.api.setStudent(studentToSet);
77 | } else {
78 | const students = await this.getStudents();
79 | if (students.length === 0) {
80 | throw new Error("Student not found!");
81 | } else {
82 | const studentToSet = students[0];
83 | this.student = studentToSet;
84 | this.api.setStudent(studentToSet);
85 | }
86 | }
87 | }
88 |
89 | public async getLessons(dateFrom?: Date, dateTo?: Date) {
90 | const dFrom = dateFrom ? dateFrom : new Date();
91 | const dTo = dateTo ? dateTo : new Date();
92 | const data = await this.api.helper.getList(
93 | DATA_TIMETABLE,
94 | false,
95 | undefined,
96 | dFrom,
97 | dTo,
98 | FilterType.BY_PUPIL
99 | );
100 | return data.map(
101 | (item: any): Lesson => new Lesson().serialize(item)
102 | ) as Lesson[];
103 | }
104 |
105 | public async getChangedLessons(dateFrom?: Date, dateTo?: Date) {
106 | const dFrom = dateFrom ? dateFrom : new Date();
107 | const dTo = dateTo ? dateTo : new Date();
108 | const data = await this.api.helper.getList(
109 | DATA_TIMETABLE_CHANGES,
110 | false,
111 | undefined,
112 | dFrom,
113 | dTo,
114 | FilterType.BY_PUPIL
115 | );
116 | return data.map(
117 | (item: any): ChangedLesson => new ChangedLesson().serialize(item)
118 | ) as ChangedLesson[];
119 | }
120 |
121 | public async getLuckyNumber(): Promise {
122 | const data = await this.api.helper.getData(DATA_LUCKY_NUMBER, {
123 | constituentId: this.api.student.school.id,
124 | day: moment().format("yyyy-mm-dd"),
125 | });
126 | return new LuckyNumber().serialize(data);
127 | }
128 |
129 | private getPeriodById(periodId: number) {
130 | if (!this.student) {
131 | throw Error("Student was undefined!");
132 | }
133 | const arr = this.student.periods.filter((period) => {
134 | return period.id === periodId;
135 | });
136 | return arr?.length === 0 ? undefined : arr[0];
137 | }
138 |
139 | public async getGrades(lastSync: Date) {
140 | const data = await this.api.helper.getList(
141 | DATA_GRADE,
142 | false,
143 | lastSync,
144 | undefined,
145 | undefined,
146 | FilterType.BY_PUPIL
147 | );
148 | return (await Promise.all(
149 | data.map(async (grade: any) =>
150 | new Grade(
151 | (periodId: number) => this.getPeriodById(periodId) as any
152 | ).serialize(grade)
153 | )
154 | )) as Grade[];
155 | }
156 | public async getMessageBoxes() {
157 | const data = await this.api.helper.getList(
158 | DATA_MESSAGEBOX,
159 | );
160 | return (Promise.all(
161 | data.map(async (messageBox: MessageBox) =>
162 | new MessageBox().serialize(messageBox)
163 | )
164 | ));
165 | }
166 | public async getMessages(messageBox: string) {
167 | const data = await this.api.helper.getList(
168 | DATA_MESSAGE,
169 | undefined,
170 | undefined,
171 | undefined,
172 | undefined,
173 | FilterType.BY_MESSAGEBOX,
174 | messageBox,
175 | 1,
176 | );
177 | return (Promise.all(
178 | data.map(async (message: Message) =>
179 | new Message().serialize(message)
180 | )
181 | ));
182 | }
183 | public async getHomework() {
184 | const data = await this.api.helper.getList(
185 | DATA_HOMEWORK,
186 | undefined,
187 | undefined,
188 | undefined,
189 | undefined,
190 | FilterType.BY_PUPIL
191 | );
192 | return (Promise.all(
193 | data.map(async (homework: Homework) =>
194 | new Homework().serialize(homework)
195 | )
196 | ));
197 | }
198 | public async getAttendance(from: Date, to: Date) {
199 | const millisInOneDay = 86400000;
200 | to.setTime(to.getTime() + millisInOneDay);
201 | const data = await this.api.helper.getList(
202 | DATA_ATTENDANCE,
203 | false,
204 | undefined,
205 | from,
206 | to,
207 | FilterType.BY_PUPIL
208 | );
209 | return (Promise.all(
210 | data.map(async (attendance: Attendance) =>
211 | new Attendance().serialize(attendance)
212 | )
213 | ));
214 | }
215 | public async getExams(lastSync?: Date): Promise {
216 | const data = await this.api.helper.getList(
217 | DATA_EXAM,
218 | false,
219 | lastSync,
220 | undefined,
221 | undefined,
222 | FilterType.BY_PUPIL
223 | );
224 | return (Promise.all(
225 | data.map(async (exam: any) =>
226 | new Exam().serialize(exam)
227 | )
228 | ));
229 | }
230 | }
231 |
--------------------------------------------------------------------------------
/src/keystore.ts:
--------------------------------------------------------------------------------
1 | import { defaultDeviceModel, getFirebaseToken, generateKeyPair } from "./utils";
2 | export class Keystore {
3 | public certificate: string | undefined;
4 | public fingerprint: string | undefined;
5 | public privateKey: string | undefined;
6 | public firebaseToken: string | undefined;
7 | public deviceModel: string | undefined;
8 |
9 | public async init(
10 | deviceModel: string = defaultDeviceModel(),
11 | firebaseToken?: string
12 | ) {
13 | if (!firebaseToken) {
14 | firebaseToken = await getFirebaseToken();
15 | }
16 | const { privateKey, certificate, fingerprint } = await generateKeyPair();
17 | this.certificate = certificate;
18 | this.fingerprint = fingerprint;
19 | this.privateKey = privateKey;
20 | this.firebaseToken = firebaseToken;
21 | this.deviceModel = deviceModel;
22 | }
23 |
24 | /**
25 | * @deprecated since version 3.0
26 | */
27 | public load(
28 | certificate: string,
29 | fingerprint: string,
30 | privateKey: string,
31 | firebaseToken: string,
32 | deviceModel: string
33 | ) {
34 | this.certificate = certificate;
35 | this.fingerprint = fingerprint;
36 | this.privateKey = privateKey;
37 | this.firebaseToken = firebaseToken;
38 | this.deviceModel = deviceModel;
39 | }
40 |
41 | public loadFromObject({
42 | certificate,
43 | fingerprint,
44 | privateKey,
45 | firebaseToken,
46 | deviceModel,
47 | }: {
48 | certificate: string;
49 | fingerprint: string;
50 | privateKey: string;
51 | firebaseToken: string;
52 | deviceModel: string;
53 | }) {
54 | this.certificate = certificate;
55 | this.fingerprint = fingerprint;
56 | this.privateKey = privateKey;
57 | this.firebaseToken = firebaseToken;
58 | this.deviceModel = deviceModel;
59 | }
60 |
61 | public loadFromJsonString(jsonString: string) {
62 | this.loadFromObject(JSON.parse(jsonString));
63 | }
64 |
65 | /**
66 | * @deprecated since version 3.2 - use `loadFromJsonString()` instead
67 | */
68 | public async loadFromJsonFile(path: string) {
69 | throw new Error("Deprecated method. Use loadFromJsonString instead.");
70 | }
71 |
72 | public dumpToObject() {
73 | return {
74 | certificate: this.certificate,
75 | fingerprint: this.fingerprint,
76 | privateKey: this.privateKey,
77 | firebaseToken: this.firebaseToken,
78 | deviceModel: this.deviceModel,
79 | };
80 | }
81 |
82 | public dumpToJsonString() {
83 | return JSON.stringify(this.dumpToObject());
84 | }
85 |
86 | /**
87 | * @deprecated since version 3.2 - use `dumpToJsonString()` instead
88 | */
89 | public async dumpToJsonFile(path: string) {
90 | throw new Error("Deprecated method. Use dumpToJsonString instead.");
91 | }
92 |
93 | /**
94 | * @deprecated since version 3.0 - use `dumpToObject()` instead
95 | */
96 | public dump() {
97 | return this.dumpToObject();
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/models/account.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 |
3 | export class Account extends Serializable {
4 | @bind("LoginId") loginId!: number;
5 | @bind("UserLogin") userLogin!: string;
6 | @bind("UserName") userName!: string;
7 | @bind("RestUrl") restUrl!: string;
8 | }
9 |
--------------------------------------------------------------------------------
/src/models/address.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 |
3 | export class Address extends Serializable {
4 | @bind('GlobalKey') globalKey: string = '';
5 | @bind('Name') name: string = '';
6 | @bind('HasRead') hasRead?: string;
7 | }
--------------------------------------------------------------------------------
/src/models/attachment.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 |
3 | export class Attachment extends Serializable {
4 | @bind("Name") name!: string;
5 | @bind("Link") link!: string;
6 | }
7 |
--------------------------------------------------------------------------------
/src/models/attendance.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 | import { Subject } from "./subject";
3 | import { Teacher } from "./teacher";
4 | import { TeamClass } from "./teamClass";
5 | import { DateTime } from "./dateTime";
6 | import { TimeSlot } from "./timeSlot";
7 | import { PresenceType } from "./presenceType";
8 | import { TeamVirtual } from "./teamVirtual";
9 |
10 | export class Attendance extends Serializable {
11 | @bind('LessonId') lessonId: number = 0;
12 | @bind('Id') id: number = 0;
13 | @bind('LessonNumber') lessonNumber: number = 0;
14 | @bind('GlobalKey') globalKey: string = '';
15 | @bind('LessonClassId') lessonClassId: number = 0;
16 | @bind('LessonClassGlobalKey') lessonClassGlobalKey: string = '';
17 | @bind('CalculatePresence') calculcatePresence: boolean = false;
18 | @bind('Replacement') replacement: boolean = false;
19 | @bind('Subject') subject: Subject = new Subject();
20 | @bind('Topic') topic: string = '';
21 | @bind('TeacherPrimary') teacher: Teacher = new Teacher();
22 | @bind('TeacherSecondary') secondTeacher?: Teacher = new Teacher();
23 | @bind('TeacherMod') mainTeacher?: Teacher = new Teacher();
24 | @bind('Clazz') teamClass?: TeamClass = new TeamClass();
25 | @bind('GroupDefinition') classAlias?: string = '';
26 | @bind('Day') date: DateTime = new DateTime();
27 | @bind('TimeSlot') time?: TimeSlot = new TimeSlot();
28 | @bind('DateModify') dateModified?: DateTime = new DateTime();
29 | @bind('AuxPresenceId') auxPresenceId?: number = 0;
30 | @bind('JustificationStatus') justificationStatus?: string = '';
31 | @bind('PresenceType') presenceType?: PresenceType;
32 | @bind('Note') note?: string;
33 | @bind('PublicResources') publicResources?: string;
34 | @bind('RemoteResources') remoteResources?: string;
35 | @bind('Distribution') group?: TeamVirtual;
36 | @bind('Visible') visible?: boolean;
37 | }
--------------------------------------------------------------------------------
/src/models/changedLesson.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 | import { DateTime } from "./dateTime";
3 | import { LessonChanges } from "./lessonChanges";
4 | import { LessonRoom } from "./lessonRoom";
5 | import { Subject } from "./subject";
6 | import { Teacher } from "./teacher";
7 | import { TeamClass } from "./teamClass";
8 | import { TeamVirtual } from "./teamVirtual";
9 | import { TimeSlot } from "./timeSlot";
10 |
11 | export class ChangedLesson extends Serializable {
12 | @bind("Id") id?: number;
13 | @bind("UnitId") unitId?: number;
14 | @bind("ScheduleId") scheduleId?: number;
15 | @bind("LessonDate") lessonDate?: DateTime;
16 | @bind("Note") note?: string;
17 | @bind("Reason") reason?: string;
18 | @bind("TimeSlot") time?: TimeSlot;
19 | @bind("Room") room?: LessonRoom;
20 | @bind("TeacherPrimary") teacher?: Teacher;
21 | @bind("TeacherSecondary") secondTeacher?: Teacher;
22 | @bind("Subject") subject?: Subject;
23 | @bind("Event") event?: string;
24 | @bind("Change") change?: LessonChanges;
25 | @bind("ChangeDate") changeDate?: DateTime;
26 | @bind("Clazz") class?: TeamClass;
27 | @bind("Distribution") distribution?: TeamVirtual;
28 | }
29 |
--------------------------------------------------------------------------------
/src/models/dateTime.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 |
3 | export class DateTime extends Serializable {
4 | @bind("Timestamp") timestamp!: number;
5 | @bind("Date") date!: string;
6 | @bind("DateDisplay") dateDisplay?: string;
7 | @bind("Time") time!: string;
8 | }
9 |
--------------------------------------------------------------------------------
/src/models/exam.ts:
--------------------------------------------------------------------------------
1 | import {bind, Serializable} from "../serialize";
2 | import {DateTime} from "./dateTime";
3 | import {Teacher} from "./teacher";
4 | import {Subject} from "./subject";
5 |
6 | export class Exam extends Serializable {
7 | @bind("Id") id: number = 0;
8 | @bind("Key") key: string = '';
9 | @bind("Type") type: string = '';
10 | @bind("Content") topic: string = '';
11 | @bind("DateCreated") dateCreated: DateTime = new DateTime();
12 | @bind("DateModify") dateModified: DateTime = new DateTime();
13 | @bind("Deadline") deadline: DateTime = new DateTime();
14 | @bind("Creator") creator: Teacher = new Teacher();
15 | @bind("Subject") subject: Subject = new Subject();
16 | @bind("PupilId") pupilId: number = 0;
17 | }
--------------------------------------------------------------------------------
/src/models/grade.ts:
--------------------------------------------------------------------------------
1 | import { bind, customBind, Serializable } from "../serialize";
2 | import { DateTime } from "./dateTime";
3 | import { GradeColumn } from "./gradeColumn";
4 | import { Period } from "./period";
5 | import { Teacher } from "./teacher";
6 |
7 | export class Grade extends Serializable {
8 | readonly periodBinder: (data: any) => Period;
9 | constructor(getPeriodById: (periodId: number) => Period) {
10 | super();
11 | this.periodBinder = getPeriodById;
12 | }
13 |
14 | @bind("Id") id!: number;
15 | @bind("Key") key!: string;
16 | @bind("PupilId") pupilId!: number;
17 | @bind("ContentRaw") contentRaw!: string;
18 | @bind("Content") content!: string;
19 | @bind("DateCreated") dateCreated!: DateTime;
20 | @bind("DateModify") dateModify!: DateTime;
21 | @bind("Creator") creator!: Teacher;
22 | @bind("Modifier") modifier!: Teacher;
23 | @customBind("Column", (data: any, thisPass: any) =>
24 | new GradeColumn(thisPass.periodBinder).serialize(data)
25 | )
26 | column!: GradeColumn;
27 | @bind("Value") value?: number;
28 | @bind("Comment") comment?: string;
29 | @bind("Numerator") numerator?: number;
30 | @bind("Denominator") denominator?: number;
31 | }
32 |
--------------------------------------------------------------------------------
/src/models/gradeCategory.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 |
3 | export class GradeCategory extends Serializable {
4 | @bind("Id") id!: string;
5 | @bind("Name") name!: string;
6 | @bind("Code") code!: string;
7 | }
8 |
--------------------------------------------------------------------------------
/src/models/gradeColumn.ts:
--------------------------------------------------------------------------------
1 | import { bind, customBind, Serializable } from "../serialize";
2 | import { GradeCategory } from "./gradeCategory";
3 | import { Period } from "./period";
4 | import { Subject } from "./subject";
5 |
6 | export class GradeColumn extends Serializable {
7 | readonly periodBinder: (data: any) => Period;
8 | constructor(getPeriodById: (periodId: number) => Period) {
9 | super();
10 | this.periodBinder = getPeriodById;
11 | }
12 |
13 | @bind("Id") id!: number;
14 | @bind("Key") key!: string;
15 | @bind("PeriodId") periodId!: number;
16 | @bind("Name") name!: string;
17 | @bind("Code") code!: string;
18 | @bind("Group") group!: string;
19 | @bind("Number") number!: number;
20 | @bind("Weight") weight!: number;
21 | @bind("Subject") subject!: Subject;
22 | @bind("Category") category?: GradeCategory;
23 | @customBind("PeriodId", (data: any, thisPass: any) => {
24 | return thisPass.periodBinder(data);
25 | })
26 | period?: Period;
27 | }
28 |
--------------------------------------------------------------------------------
/src/models/homework.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 | import { Teacher } from "./teacher";
3 | import { Subject } from "./subject";
4 | import { Attachment } from "./attachment";
5 | export class Homework extends Serializable {
6 | @bind('Id') id: number = 0;
7 | @bind('Key') key: string = '';
8 | @bind('IdHomework') homeworkId: number = 0;
9 | @bind('Content') content: string = '';
10 | @bind('DateCreated') dateCreated: Date = new Date();
11 | @bind('Creator') creator: Teacher = new Teacher();
12 | @bind('Subject') subject: Subject = new Subject();
13 | @bind('Attachments') attachments: Attachment[] = [];
14 | @bind('IsAnswerRequired') isAnswerRequired: Subject = new Subject();
15 | @bind('Deadline') deadline: Date = new Date();
16 | @bind('AnswerDeadline') answerDeadline: Date = new Date();
17 | @bind('AnswerDate') answerDate: Date = new Date();
18 | }
19 |
--------------------------------------------------------------------------------
/src/models/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./account";
2 | export * from "./address";
3 | export * from "./attendance";
4 | export * from "./luckyNumber";
5 | export * from "./attachment";
6 | export * from "./dateTime";
7 | export * from "./exam";
8 | export * from "./homework";
9 | export * from "./message";
10 | export * from "./messageBox";
11 | export * from "./presenceType";
12 | export * from "./timeSlot";
13 | export * from "./teamVirtual";
14 | export * from "./teamClass";
15 | export * from "./unit";
16 | export * from "./teacher";
17 | export * from "./subject";
18 | export * from "./pupil";
19 | export * from "./period";
20 | export * from "./school";
21 | export * from "./student";
22 | export * from "./lessonRoom";
23 | export * from "./lessonChanges";
24 | export * from "./lesson";
25 | export * from "./gradeCategory";
26 | export * from "./gradeColumn";
27 | export * from "./grade";
28 | export * from "./changedLesson";
29 |
--------------------------------------------------------------------------------
/src/models/lesson.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 | import { DateTime } from "./dateTime";
3 | import { LessonChanges } from "./lessonChanges";
4 | import { LessonRoom } from "./lessonRoom";
5 | import { Subject } from "./subject";
6 | import { Teacher } from "./teacher";
7 | import { TeamClass } from "./teamClass";
8 | import { TeamVirtual } from "./teamVirtual";
9 | import { TimeSlot } from "./timeSlot";
10 |
11 | export class Lesson extends Serializable {
12 | @bind("Id") id?: number;
13 | @bind("Date") date?: DateTime;
14 | @bind("TimeSlot") timeSlot?: TimeSlot;
15 | @bind("Room") room?: LessonRoom;
16 | @bind("TeacherPrimary") teacherPrimary?: Teacher;
17 | @bind("TeacherSecondary") teacherSecondary?: Teacher;
18 | @bind("Subject") subject?: Subject;
19 | @bind("Event") event?: string;
20 | @bind("Change") change?: LessonChanges;
21 | @bind("Clazz") class?: TeamClass;
22 | @bind("PupilAlias") pupilAlias?: string;
23 | @bind("Distribution") distribution?: TeamVirtual;
24 | @bind("Visible") visible?: boolean;
25 | }
26 |
--------------------------------------------------------------------------------
/src/models/lessonChanges.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 |
3 | export class LessonChanges extends Serializable {
4 | @bind("Id") id!: number;
5 | @bind("Type") type!: number;
6 | @bind("Separation") separation!: boolean;
7 | }
8 |
--------------------------------------------------------------------------------
/src/models/lessonRoom.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 |
3 | export class LessonRoom extends Serializable {
4 | @bind("Id") id!: number;
5 | @bind("Code") code!: string;
6 | }
7 |
--------------------------------------------------------------------------------
/src/models/luckyNumber.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 |
3 | export class LuckyNumber extends Serializable {
4 | @bind("Day") day!: string;
5 | @bind("Number") number!: string;
6 | }
7 |
--------------------------------------------------------------------------------
/src/models/message.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 | import { Address } from "./address";
3 | import { Attachment } from "./attachment";
4 |
5 | export class Message extends Serializable {
6 | constructor() {
7 | super();
8 | }
9 | @bind('Id') id: string = '';
10 | @bind('GlobalKey') globalKey: string = '';
11 | @bind('ThreadKey') threadKey: string = '';
12 | @bind('Subject') subject: string = '';
13 | @bind('Content') content: string = '';
14 | @bind('DateSent') sentDate: Date = new Date();
15 | @bind('Status') status: number = 0;
16 | @bind('Sender') sender: string = '';
17 | @bind('Receiver') receivers: Address[] = [];
18 | @bind('Attachments') attachments: Attachment[] = [];
19 | @bind('DateRead') readDate?: Date = new Date();
20 | }
--------------------------------------------------------------------------------
/src/models/messageBox.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 | export class MessageBox extends Serializable {
3 | constructor() {
4 | super();
5 | }
6 | @bind("Id") id?: number;
7 | @bind('GlobalKey') globalKey: string = '';
8 | @bind('Name') name: string = '';
9 | }
10 |
--------------------------------------------------------------------------------
/src/models/period.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 |
3 | export class Period extends Serializable {
4 | @bind("Id") id!: number;
5 | @bind("Level") level!: number;
6 | @bind("Number") number!: number;
7 | @bind("Current") current!: boolean;
8 | @bind("Last") last!: boolean;
9 | @bind("Start") start!: any;
10 | @bind("End") end!: any;
11 | }
12 |
--------------------------------------------------------------------------------
/src/models/presenceType.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 |
3 | export class PresenceType extends Serializable {
4 |
5 | @bind('Id') id: number = 0;
6 | @bind('Name') name: string = '';
7 | @bind('Symbol') symbol: string = '';
8 | @bind('CategoryId') category_id: number = 0;
9 | @bind('CategoryName') category_name: string = '';
10 | @bind('Position') position: number = 0;
11 | @bind('Presence') presence: boolean = false;
12 | @bind('Absence') absence: boolean = false;
13 | @bind('LegalAbsence') exemption: boolean = false;
14 | @bind('Late') late: boolean = false;
15 | @bind('AbsenceJustified') justified: boolean = false;
16 | @bind('Removed') deleted: boolean = false;
17 | }
--------------------------------------------------------------------------------
/src/models/pupil.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 |
3 | export class Pupil extends Serializable {
4 | @bind("Id") id!: number;
5 | @bind("LoginId") loginId!: number;
6 | @bind("LoginValue") loginValue!: string;
7 | @bind("FirstName") firstName!: string;
8 | @bind("Surname") surname!: string;
9 | @bind("Sex") sex!: boolean;
10 | @bind("SecondName") secondName?: string;
11 | }
12 |
--------------------------------------------------------------------------------
/src/models/school.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 |
3 | export class School extends Serializable {
4 | @bind("Id") id!: number;
5 | @bind("Name") name!: string;
6 | @bind("Short") short!: string;
7 | @bind("Address") address!: string;
8 | }
9 |
--------------------------------------------------------------------------------
/src/models/student.ts:
--------------------------------------------------------------------------------
1 | import { bind, customBind, Serializable } from "../serialize";
2 | import { Period } from "./period";
3 | import { Pupil } from "./pupil";
4 | import { School } from "./school";
5 | import { Unit } from "./unit";
6 |
7 | const bindPeriods = (data: any[]) => {
8 | return data.map((period: any) => new Period().serialize(period));
9 | };
10 |
11 | export class Student extends Serializable {
12 | @bind("TopLevelPartition") symbol!: string;
13 | @bind("Partition") symbol_code!: string;
14 | @bind("Pupil") pupil!: Pupil;
15 | @bind("Unit") unit!: Unit;
16 | @bind("ConstituentUnit") school!: School;
17 | @customBind("Periods", bindPeriods) periods!: Period[];
18 | }
19 |
--------------------------------------------------------------------------------
/src/models/subject.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 |
3 | export class Subject extends Serializable {
4 | @bind("Id") id!: number;
5 | @bind("Key") key!: string;
6 | @bind("Name") name!: string;
7 | @bind("Kod") code!: string;
8 | @bind("Position") position!: number;
9 | }
10 |
--------------------------------------------------------------------------------
/src/models/teacher.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 |
3 | export class Teacher extends Serializable {
4 | @bind("Id") id!: number;
5 | @bind("Name") name!: string;
6 | @bind("Surname") surname!: string;
7 | @bind("DisplayName") displayName!: string;
8 | }
9 |
--------------------------------------------------------------------------------
/src/models/teamClass.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 |
3 | export class TeamClass extends Serializable {
4 | @bind("Id") id!: number;
5 | @bind("Key") key!: string;
6 | @bind("DisplayName") displayName!: string;
7 | @bind("Symbol") symbol!: string;
8 | }
9 |
--------------------------------------------------------------------------------
/src/models/teamVirtual.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 |
3 | export class TeamVirtual extends Serializable {
4 | @bind("Id") id!: number;
5 | @bind("Key") key!: string;
6 | @bind("Shortcut") shortcut!: string;
7 | @bind("Name") name!: string;
8 | @bind("PartType") partType!: string;
9 | }
10 |
--------------------------------------------------------------------------------
/src/models/timeSlot.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 |
3 | export class TimeSlot extends Serializable {
4 | @bind("Id") id!: number;
5 | @bind("Start") start!: string;
6 | @bind("End") end!: string;
7 | @bind("Display") display!: string;
8 | @bind("Position") position!: number;
9 | }
10 |
--------------------------------------------------------------------------------
/src/models/unit.ts:
--------------------------------------------------------------------------------
1 | import { bind, Serializable } from "../serialize";
2 |
3 | export class Unit extends Serializable {
4 | @bind("Id") id!: number;
5 | @bind("Symbol") symbol!: string;
6 | @bind("Name") name!: string;
7 | @bind("Short") short!: string;
8 | @bind("DisplayName") displayName!: string;
9 | @bind("Address") address!: string;
10 | @bind("RestURL") restUrl!: string;
11 | }
12 |
--------------------------------------------------------------------------------
/src/serialize.ts:
--------------------------------------------------------------------------------
1 | import "reflect-metadata";
2 |
3 | const bindMetadataKey = Symbol("bind");
4 | const skipBindingMetadataKey = Symbol("bindSkip");
5 | const customBindMetadataKey = Symbol("type");
6 |
7 | export const bind = function (bindName: string) {
8 | return Reflect.metadata(bindMetadataKey, bindName);
9 | };
10 |
11 | export const customBind = function (
12 | bindName: string,
13 | binder: ((data: any) => any) | ((data: any, thisPass: any) => any)
14 | ) {
15 | return Reflect.metadata(customBindMetadataKey, { bindName, binder });
16 | };
17 |
18 | export abstract class Serializable {
19 | public serialize(source: any) {
20 | if (source === null) {
21 | return null as any;
22 | }
23 | Object.keys(this).forEach((srcKey) => {
24 | const destKey = Reflect.getMetadata(bindMetadataKey, this, srcKey);
25 | const typeData = Reflect.getMetadata("design:type", this, srcKey);
26 | const customBind = Reflect.getMetadata(
27 | customBindMetadataKey,
28 | this,
29 | srcKey
30 | );
31 | if (!destKey && !customBind) return;
32 | if (customBind) {
33 | this[srcKey as keyof this] = customBind.binder(
34 | source[customBind.bindName as keyof typeof source],
35 | this
36 | );
37 | return;
38 | }
39 | const srcObj = source[destKey as keyof typeof source];
40 | this[srcKey as keyof this] =
41 | typeData.prototype instanceof Serializable
42 | ? new (typeData as any)().serialize(srcObj)
43 | : srcObj;
44 | });
45 | return this;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/signer.ts:
--------------------------------------------------------------------------------
1 | import forge from "node-forge";
2 | import { Buffer } from "buffer/";
3 |
4 | function getDigest(body: any) {
5 | if (body == null) return "";
6 | return forge.util.encode64(
7 | forge.md.sha256.create().update(body).digest().bytes()
8 | ); // base64
9 | }
10 |
11 | function getSignatureValue(values: any, pkey: any) {
12 | const messageDigest = forge.md.sha256.create();
13 | messageDigest.update(values);
14 | const key = forge.pki
15 | .privateKeyFromPem(
16 | "-----BEGIN PRIVATE KEY-----\n" + pkey + "\n-----END PRIVATE KEY-----"
17 | )
18 | .sign(messageDigest);
19 | return Buffer.from(forge.util.binary.raw.decode(key)).toString("base64");
20 | }
21 |
22 | function getEncodedPath(path: any) {
23 | const url = path.match("(api/mobile/.+)");
24 | if (url == null)
25 | throw new Error(
26 | "The URL does not seem correct (does not match `(api/mobile/.+)` regex)"
27 | );
28 |
29 | return encodeURIComponent(url[0]).toLowerCase();
30 | }
31 |
32 | function getHeadersList(
33 | body: any,
34 | digest: any,
35 | canonicalUrl: any,
36 | timestamp: any
37 | ) {
38 | const signData = [
39 | ["vCanonicalUrl", canonicalUrl],
40 | body == null ? null : ["Digest", digest],
41 | ["vDate", new Date(timestamp).toUTCString()],
42 | ].filter((item) => !!item);
43 |
44 | return {
45 | headers: signData.map((item) => item![0]).join(" "),
46 | values: signData.map((item) => item![1]).join(""),
47 | };
48 | }
49 |
50 | export interface SignatureValues {
51 | digest: string;
52 | canonicalUrl: string;
53 | signature: string;
54 | }
55 |
56 | function getSignatureValues(
57 | fingerprint: string,
58 | privateKey: string,
59 | body: string,
60 | requestPath: string,
61 | timestamp: number | string
62 | ): SignatureValues {
63 | const canonicalUrl = getEncodedPath(requestPath);
64 | const digest = getDigest(body);
65 | const { headers, values } = getHeadersList(
66 | body,
67 | digest,
68 | canonicalUrl,
69 | timestamp
70 | );
71 | const signatureValue = getSignatureValue(values, privateKey);
72 |
73 | return {
74 | digest: `SHA-256=${digest}`,
75 | canonicalUrl: canonicalUrl,
76 | signature: `keyId="${fingerprint}",headers="${headers}",algorithm="sha256withrsa",signature=Base64(SHA256withRSA(${signatureValue}))`,
77 | };
78 | }
79 |
80 | export { getSignatureValues };
81 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 |
3 | import forge from "node-forge";
4 |
5 | import { v4 as uuidv4, v5 as uuidv5 } from "uuid";
6 | import axios from "axios";
7 | import qs from "querystring";
8 | import { Account } from "./models";
9 |
10 | export const now = () => {
11 | return Math.floor(Date.now() / 1000);
12 | };
13 | export const uuid = (seed?: string) => {
14 | if (seed) {
15 | return uuidv5(seed, "6ba7b814-9dad-11d1-80b4-00c04fd430c8");
16 | }
17 | return uuidv4();
18 | };
19 | export const getComponents = async () => {
20 | let r = await axios.get(
21 | "http://komponenty.vulcan.net.pl/UonetPlusMobile/RoutingRules.txt"
22 | );
23 | const components: Array = r.data.split("\r\n");
24 | let objToReturn: any = {};
25 | for (let i = 0; i < components.length; i++) {
26 | objToReturn[components[i].split(",")[0]] = components[i].split(",")[1];
27 | }
28 | return objToReturn;
29 | };
30 | export const getFirebaseToken = async () => {
31 | const aid = "4609707972546570896:3626695765779152704";
32 | const device = aid.split(":")[0];
33 | const app = "pl.vulcan.uonetmobile";
34 | const data = {
35 | sender: "987828170337",
36 | "X-scope": "*",
37 | "X-gmp_app_id": "1:987828170337:android:ac97431a0a4578c3",
38 | app: app,
39 | device: device,
40 | };
41 |
42 | const headers = {
43 | Authorization: `AidLogin ${aid}`,
44 | "User-Agent": "Android-GCM/1.5",
45 | app: app,
46 | "Content-Type": "application/x-www-form-urlencoded",
47 | };
48 |
49 | let r = await axios.post(
50 | "https://android.clients.google.com/c2dm/register3",
51 | qs.stringify(data),
52 | { headers: headers }
53 | );
54 |
55 | return r.data.split("=")[1];
56 | };
57 |
58 | export async function getBaseUrl(token: string) {
59 | let code = token.substr(0, 3);
60 | let components = await getComponents();
61 | if (components[code] !== undefined) {
62 | return components[code];
63 | } else {
64 | throw "Niepoprawny token!";
65 | }
66 | }
67 | export const APP_NAME = "DzienniczekPlus 2.0";
68 | export const APP_VERSION = "1.4.2";
69 | export const APP_OS = "Android";
70 | export const APP_USER_AGENT = "Dart/2.10 (dart:io)";
71 |
72 | export const defaultDeviceModel = () => `Vulcan API (Node ${process.version})`;
73 |
74 | export const millis = () => Date.now();
75 |
76 | export const generateKeyPair = async () => {
77 | const addYears = (dt: Date, n: number) =>
78 | new Date(dt.setFullYear(dt.getFullYear() + n));
79 |
80 | const pki = forge.pki;
81 |
82 | const keys: any = await new Promise((resolve, reject) => {
83 | forge.pki.rsa.generateKeyPair(
84 | { bits: 2048, workers: 2 },
85 | (err, keypair) => {
86 | if (err) {
87 | reject(err);
88 | } else {
89 | resolve(keypair);
90 | }
91 | }
92 | );
93 | });
94 | const cert = pki.createCertificate();
95 | cert.publicKey = keys.publicKey;
96 | cert.privateKey = keys.privateKey;
97 | cert.serialNumber = "1";
98 | cert.validity.notBefore = new Date();
99 | cert.validity.notAfter = addYears(new Date(), 20);
100 | const attrs = [
101 | {
102 | shortName: "CN",
103 | value: "APP_CERTIFICATE CA Certificate",
104 | },
105 | ];
106 | cert.setSubject(attrs);
107 | cert.setIssuer(attrs);
108 | cert.sign(cert.privateKey, forge.md.sha256.create());
109 |
110 | const fHash = forge.md.sha1.create();
111 | fHash.update(forge.asn1.toDer(pki.certificateToAsn1(cert)).getBytes());
112 | const fingerprint = fHash.digest().toHex();
113 |
114 | const privateKey = pki.privateKeyToAsn1(keys.privateKey);
115 | const privateKeyInfo = pki.wrapRsaPrivateKey(privateKey);
116 | const privateKeyPem = pki.privateKeyInfoToPem(privateKeyInfo);
117 | const certificate = pki
118 | .certificateToPem(cert)
119 | .replace("-----BEGIN CERTIFICATE-----", "")
120 | .replace("-----END CERTIFICATE-----", "")
121 | .replace(/\r?\n|\r/g, "")
122 | .trim();
123 | const privateKeyToReturn = privateKeyPem
124 | .replace("-----BEGIN PRIVATE KEY-----", "")
125 | .replace("-----END PRIVATE KEY-----", "")
126 | .replace(/\r?\n|\r/g, "")
127 | .trim();
128 | return { certificate, fingerprint, privateKey: privateKeyToReturn };
129 | };
130 |
131 | export const nowIso = () => moment().format('YYYY-MM-DD HH:mm:ss');
132 |
133 | export class AccountTools {
134 | public static loadFromObject(account: {
135 | loginId: number;
136 | userLogin: string;
137 | userName: string;
138 | restUrl: string;
139 | }): Account {
140 | const accountToReturn = new Account();
141 | accountToReturn.loginId = account.loginId;
142 | accountToReturn.userLogin = account.userLogin;
143 | accountToReturn.userName = account.userName;
144 | accountToReturn.restUrl = account.restUrl;
145 | return accountToReturn;
146 | }
147 |
148 | public static loadFromJsonString(jsonString: string) {
149 | return this.loadFromObject(JSON.parse(jsonString));
150 | }
151 |
152 | /**
153 | * @deprecated since version 3.2
154 | */
155 | public static async loadFromJsonFile(path: string) {
156 | throw new Error("Deprecated method. Use loadFromJsonString instead.");
157 | }
158 |
159 | public static dumpToObject(account: Account) {
160 | return {
161 | loginId: account.loginId,
162 | userLogin: account.userLogin,
163 | userName: account.userName,
164 | restUrl: account.restUrl,
165 | };
166 | }
167 |
168 | public static dumpToJsonString(account: Account) {
169 | return JSON.stringify(this.dumpToObject(account));
170 | }
171 |
172 | /**
173 | * @deprecated since version 3.2
174 | */
175 | public static async dumpToJsonFile(account: Account, path: string) {
176 | throw new Error("Deprecated method. Use dumpToJsonString instead.");
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Basic Options */
4 | "target": "esnext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */,
5 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
6 | // "lib": [], /* Specify library files to be included in the compilation. */
7 | // "allowJs": true, /* Allow javascript files to be compiled. */
8 | // "checkJs": true, /* Report errors in .js files. */
9 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
10 | "declaration": true /* Generates corresponding '.d.ts' file. */,
11 | "emitDeclarationOnly": true,
12 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
13 | // "sourceMap": true, /* Generates corresponding '.map' file. */
14 | // "outFile": "./", /* Concatenate and emit output to single file. */
15 | "outDir": "lib" /* Redirect output structure to the directory. */,
16 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
17 | // "composite": true, /* Enable project compilation */
18 | // "removeComments": true, /* Do not emit comments to output. */
19 | // "noEmit": true, /* Do not emit outputs. */
20 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
21 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
22 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
23 | /* Strict Type-Checking Options */
24 | "strict": true /* Enable all strict type-checking options. */,
25 | "resolveJsonModule": true,
26 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
27 | // "strictNullChecks": true, /* Enable strict null checks. */
28 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
29 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
30 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
31 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
32 | /* Additional Checks */
33 | // "noUnusedLocals": true, /* Report errors on unused locals. */
34 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
35 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
36 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
37 | /* Module Resolution Options */
38 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
39 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
40 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
41 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
42 | // "typeRoots": [], /* List of folders to include type definitions from. */
43 | // "types": [], /* Type declaration files to be included in compilation. */
44 | "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */,
45 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
46 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
47 | /* Source Map Options */
48 | // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
49 | // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
50 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
51 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
52 | /* Experimental Options */
53 | "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
54 | "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */
55 | /* Advanced Options */
56 | // "declarationDir": "lib" /* Output directory for generated declaration files. */
57 | },
58 | "include": ["src"]
59 | }
60 |
--------------------------------------------------------------------------------