nomagick commited on
Commit
89d6d49
·
unverified ·
1 Parent(s): 8b9ecf2
.github/workflows/.keep ADDED
File without changes
.gitignore CHANGED
@@ -1,130 +1,3 @@
1
- # Logs
2
- logs
3
- *.log
4
- npm-debug.log*
5
- yarn-debug.log*
6
- yarn-error.log*
7
- lerna-debug.log*
8
- .pnpm-debug.log*
9
-
10
- # Diagnostic reports (https://nodejs.org/api/report.html)
11
- report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12
-
13
- # Runtime data
14
- pids
15
- *.pid
16
- *.seed
17
- *.pid.lock
18
-
19
- # Directory for instrumented libs generated by jscoverage/JSCover
20
- lib-cov
21
-
22
- # Coverage directory used by tools like istanbul
23
- coverage
24
- *.lcov
25
-
26
- # nyc test coverage
27
- .nyc_output
28
-
29
- # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30
- .grunt
31
-
32
- # Bower dependency directory (https://bower.io/)
33
- bower_components
34
-
35
- # node-waf configuration
36
- .lock-wscript
37
-
38
- # Compiled binary addons (https://nodejs.org/api/addons.html)
39
- build/Release
40
-
41
- # Dependency directories
42
  node_modules/
43
- jspm_packages/
44
-
45
- # Snowpack dependency directory (https://snowpack.dev/)
46
- web_modules/
47
-
48
- # TypeScript cache
49
- *.tsbuildinfo
50
-
51
- # Optional npm cache directory
52
- .npm
53
-
54
- # Optional eslint cache
55
- .eslintcache
56
-
57
- # Optional stylelint cache
58
- .stylelintcache
59
-
60
- # Microbundle cache
61
- .rpt2_cache/
62
- .rts2_cache_cjs/
63
- .rts2_cache_es/
64
- .rts2_cache_umd/
65
-
66
- # Optional REPL history
67
- .node_repl_history
68
-
69
- # Output of 'npm pack'
70
- *.tgz
71
-
72
- # Yarn Integrity file
73
- .yarn-integrity
74
-
75
- # dotenv environment variable files
76
- .env
77
- .env.development.local
78
- .env.test.local
79
- .env.production.local
80
- .env.local
81
-
82
- # parcel-bundler cache (https://parceljs.org/)
83
- .cache
84
- .parcel-cache
85
-
86
- # Next.js build output
87
- .next
88
- out
89
-
90
- # Nuxt.js build / generate output
91
- .nuxt
92
- dist
93
-
94
- # Gatsby files
95
- .cache/
96
- # Comment in the public line in if your project uses Gatsby and not Next.js
97
- # https://nextjs.org/blog/next-9-1#public-directory-support
98
- # public
99
-
100
- # vuepress build output
101
- .vuepress/dist
102
-
103
- # vuepress v2.x temp and cache directory
104
- .temp
105
- .cache
106
-
107
- # Docusaurus cache and generated files
108
- .docusaurus
109
-
110
- # Serverless directories
111
- .serverless/
112
-
113
- # FuseBox cache
114
- .fusebox/
115
-
116
- # DynamoDB Local files
117
- .dynamodb/
118
-
119
- # TernJS port file
120
- .tern-port
121
-
122
- # Stores VSCode versions used for testing VSCode extensions
123
- .vscode-test
124
-
125
- # yarn v2
126
- .yarn/cache
127
- .yarn/unplugged
128
- .yarn/build-state.yml
129
- .yarn/install-state.gz
130
- .pnp.*
 
1
+ package-lock.json
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  node_modules/
3
+ .DS_Store
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
.gitmodules ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ [submodule "thinapps-shared"]
2
+ path = thinapps-shared
3
+ url = git@github.com:jina-ai/thinapps-shared.git
.vscode/exensions.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "recommendations": [
3
+ "editorconfig.editorconfig",
4
+ "octref.vetur",
5
+ "redhat.vscode-yaml",
6
+ "dbaeumer.vscode-eslint",
7
+ "esbenp.prettier-vscode",
8
+ "streetsidesoftware.code-spell-checker"
9
+ ]
10
+ }
.vscode/launch.json ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+ {
5
+ "name": "Debug Fullstack: attach",
6
+ "request": "attach",
7
+ "cwd": "${workspaceFolder}/backend/functions",
8
+ "skipFiles": [
9
+ "<node_internals>/**"
10
+ ],
11
+ "type": "node",
12
+ "preLaunchTask": "Fullstack:debug"
13
+ },
14
+ {
15
+ "name": "Debug Fullstack: attach: with proxy",
16
+ "request": "attach",
17
+ "cwd": "${workspaceFolder}/backend/functions",
18
+ "skipFiles": [
19
+ "<node_internals>/**"
20
+ ],
21
+ "type": "node",
22
+ "preLaunchTask": "Fullstack:debug:with-proxy"
23
+ },
24
+ {
25
+ "name": "Attach",
26
+ "port": 9229,
27
+ "request": "attach",
28
+ "skipFiles": [
29
+ "<node_internals>/**"
30
+ ],
31
+ "type": "node"
32
+ },
33
+ {
34
+ "name": "Attach by Process ID",
35
+ "processId": "${command:PickProcess}",
36
+ "request": "attach",
37
+ "skipFiles": [
38
+ "<node_internals>/**"
39
+ ],
40
+ "type": "node"
41
+ },
42
+ {
43
+ "name": "Debug Fullstack",
44
+ "request": "launch",
45
+ "runtimeArgs": [
46
+ "emulators:start",
47
+ "--import=../.firebase-emu",
48
+ "--export-on-exit=../.firebase-emu",
49
+ ],
50
+ "cwd": "${workspaceFolder}/backend/functions",
51
+ "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/firebase",
52
+ "skipFiles": [
53
+ "<node_internals>/**"
54
+ ],
55
+ "type": "node",
56
+ "preLaunchTask": "Fullstack:prepare",
57
+ "killBehavior": "polite"
58
+ },
59
+ ]
60
+ }
.vscode/settings.json ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "editor.wordWrap": "on",
3
+ "editor.wordWrapColumn": 120,
4
+ "files.trimTrailingWhitespace": true,
5
+ "files.trimFinalNewlines": true,
6
+ "[javascript]": {
7
+ "editor.defaultFormatter": "vscode.typescript-language-features"
8
+ },
9
+ "[vue]": {
10
+ "editor.defaultFormatter": "Vue.volar"
11
+ },
12
+ "[jsonc]": {
13
+ "editor.defaultFormatter": "vscode.json-language-features"
14
+ },
15
+ "[typescript]": {
16
+ "editor.defaultFormatter": "vscode.typescript-language-features"
17
+ },
18
+ "[json]": {
19
+ "editor.defaultFormatter": "vscode.json-language-features"
20
+ },
21
+ "[yaml]": {
22
+ "editor.defaultFormatter": "redhat.vscode-yaml"
23
+ },
24
+ "[markdown]": {
25
+ "files.trimTrailingWhitespace": false
26
+ },
27
+ "typescript.tsdk": "node_modules/typescript/lib",
28
+ "vetur.format.defaultFormatter.ts": "vscode-typescript",
29
+ "vetur.format.defaultFormatter.js": "vscode-typescript",
30
+ "typescript.preferences.quoteStyle": "single",
31
+ "typescript.format.semicolons": "insert",
32
+ "typescript.preferences.importModuleSpecifier": "project-relative",
33
+ "typescript.locale": "en",
34
+ "cSpell.enabled": true,
35
+ "cSpell.words": [
36
+ "Apiextensions",
37
+ "apihubble",
38
+ "auths",
39
+ "AUTOCASTABLE",
40
+ "Autocasting",
41
+ "backchannel",
42
+ "bodyparser",
43
+ "bson",
44
+ "BUILDKIT",
45
+ "buildx",
46
+ "castable",
47
+ "cmdl",
48
+ "Commandline",
49
+ "conpty",
50
+ "cpid",
51
+ "deferreds",
52
+ "DEVBOT",
53
+ "dockerhub",
54
+ "entrypoint",
55
+ "ENVIROMENT",
56
+ "finetuner",
57
+ "fpath",
58
+ "fswalk",
59
+ "Grafana",
60
+ "Hasher",
61
+ "istio",
62
+ "jina",
63
+ "jinahub",
64
+ "jinameta",
65
+ "Knative",
66
+ "kourier",
67
+ "kube",
68
+ "kubectl",
69
+ "Kubernetes",
70
+ "kwargs",
71
+ "letsencrypt",
72
+ "liveconfigs",
73
+ "LOGNAME",
74
+ "metas",
75
+ "Mgmt",
76
+ "middlewares",
77
+ "minikube",
78
+ "minio",
79
+ "ndjson",
80
+ "nodelib",
81
+ "oidc",
82
+ "openapi",
83
+ "paramtypes",
84
+ "penv",
85
+ "pino",
86
+ "prebuild",
87
+ "quickstart",
88
+ "reinit",
89
+ "sslip",
90
+ "subval",
91
+ "Succ",
92
+ "timedout",
93
+ "TOTP",
94
+ "tsbuildinfo",
95
+ "tsyringe",
96
+ "typeclass",
97
+ "upsert",
98
+ "upserted",
99
+ "userinfo",
100
+ "Vecs",
101
+ "vectorize",
102
+ "WECHAT",
103
+ "WXPAY"
104
+ ],
105
+ }
.vscode/tasks.json ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "version": "2.0.0",
3
+ "tasks": [
4
+ {
5
+ "type": "npm",
6
+ "script": "build",
7
+ "group": "build",
8
+ "options": {
9
+ "cwd": "${workspaceFolder}/backend/functions"
10
+ },
11
+ "problemMatcher": [],
12
+ "label": "Backend:rebuild",
13
+ "detail": "Backend:rebuild"
14
+ },
15
+ {
16
+ "type": "npm",
17
+ "script": "emu:reset",
18
+ "group": "build",
19
+ "options": {
20
+ "cwd": "${workspaceFolder}/backend/functions"
21
+ },
22
+ "problemMatcher": [],
23
+ "label": "Backend:reset-emulator",
24
+ "detail": "Backend:reset-emulator"
25
+ },
26
+ {
27
+ "type": "typescript",
28
+ "options": {
29
+ "cwd": "${workspaceFolder}/backend/functions"
30
+ },
31
+ "tsconfig": "backend/functions/tsconfig.json",
32
+ "option": "watch",
33
+ "isBackground": true,
34
+ "problemMatcher": [
35
+ "$tsc-watch"
36
+ ],
37
+ "group": "build",
38
+ "label": "Backend:build:watch"
39
+ },
40
+ {
41
+ "type": "npm",
42
+ "script": "emu:debug",
43
+ "group": "none",
44
+ "options": {
45
+ "cwd": "${workspaceFolder}/backend/functions"
46
+ },
47
+ "problemMatcher": [
48
+ {
49
+ "base": "$tsc",
50
+ "background": {
51
+ "activeOnStart": false,
52
+ "beginsPattern": "shutdown requested|Starting emulators",
53
+ "endsPattern": "Debugger listening"
54
+ }
55
+ }
56
+ ],
57
+ "label": "Backend:start-emulator-debug",
58
+ "detail": "Backend:start-emulator-debug",
59
+ "dependsOn": [
60
+ "Backend:build:watch"
61
+ ],
62
+ "isBackground": true,
63
+ },
64
+ {
65
+ "type": "npm",
66
+ "script": "dev",
67
+ "options": {
68
+ "cwd": "${workspaceFolder}/webapp",
69
+ },
70
+ "group": "build",
71
+ "label": "Frontend:start:dev",
72
+ "detail": "Frontend:start:dev",
73
+ "isBackground": true,
74
+ "problemMatcher": {
75
+ "base": "$vite",
76
+ "background": {
77
+ "activeOnStart": true,
78
+ "endsPattern": "OK",
79
+ "beginsPattern": "vite"
80
+ }
81
+ },
82
+ },
83
+ {
84
+ "type": "npm",
85
+ "script": "dev",
86
+ "options": {
87
+ "cwd": "${workspaceFolder}/webapp",
88
+ "env": {
89
+ "FIREBASE_EMULATE": "true",
90
+ }
91
+ },
92
+ "group": "build",
93
+ "label": "Frontend:start:emu",
94
+ "detail": "Frontend:start:emu",
95
+ "isBackground": true,
96
+ "problemMatcher": {
97
+ "base": "$vite",
98
+ "background": {
99
+ "activeOnStart": true,
100
+ "endsPattern": "OK",
101
+ "beginsPattern": "vite"
102
+ }
103
+ },
104
+ },
105
+ {
106
+ "type": "npm",
107
+ "script": "emu:debug2",
108
+ "group": "none",
109
+ "options": {
110
+ "cwd": "${workspaceFolder}/backend/functions",
111
+ "env": {
112
+ "https_proxy": "http://127.0.0.1:7890",
113
+ "http_proxy": "http://127.0.0.1:7890",
114
+ "all_proxy": "socks5://127.0.0.1:7890"
115
+ }
116
+ },
117
+ "problemMatcher": [
118
+ {
119
+ "base": "$tsc",
120
+ "background": {
121
+ "activeOnStart": false,
122
+ "beginsPattern": "shutdown requested|Starting emulators",
123
+ "endsPattern": "Debugger listening"
124
+ }
125
+ }
126
+ ],
127
+ "label": "Backend:start-emulator-debug:with-proxy",
128
+ "detail": "Backend:start-emulator-debug:with-proxy",
129
+ "dependsOn": [
130
+ "Backend:build:watch"
131
+ ],
132
+ "isBackground": true,
133
+ },
134
+ {
135
+ "label": "Fullstack:prepare",
136
+ "dependsOn": [
137
+ "Frontend:start:emu",
138
+ "Backend:build:watch",
139
+ ],
140
+ },
141
+ {
142
+ "label": "Fullstack:debug",
143
+ "dependsOn": [
144
+ // "Frontend:start:emu",
145
+ "Backend:start-emulator-debug",
146
+ ],
147
+ },
148
+ {
149
+ "label": "Fullstack:debug:with-proxy",
150
+ "dependsOn": [
151
+ "Frontend:start:emu",
152
+ "Backend:start-emulator-debug:with-proxy",
153
+ ],
154
+ }
155
+ ]
156
+ }
README.md CHANGED
@@ -1 +1,112 @@
1
- # url2text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Url2Text
2
+
3
+ ## Development Guide
4
+
5
+ ### Prerequisite
6
+ - Node v18 (The build fails for Node version >18)
7
+ - Yarn
8
+ - Firebase CLI (`npm install -g firebase-tools`)
9
+
10
+ ### Installation
11
+
12
+ Clone the scenex repo by running the command:
13
+
14
+ ```bash
15
+ git clone git@github.com:jina-ai/url2text.git
16
+ git submodule init
17
+ git submodule update
18
+ ```
19
+
20
+ After a successful clone, install the packages for backend and the webapp.
21
+
22
+ For backend, go to the `backend/functions` directory and install the npm dependencies.
23
+
24
+ ```bash
25
+ cd backend/functions
26
+ npm install
27
+ ```
28
+
29
+ For the frontend (webapp), go to the `webapp` directory and install the yarn dependencies.
30
+
31
+ ```bash
32
+ cd webapp
33
+ yarn
34
+ ```
35
+
36
+ ### Configure
37
+
38
+ **Establish localhost connection:**
39
+
40
+ Once the packages are installed, go to the `App.vue` file inside the `webapp/src/` and uncomment the below code:
41
+
42
+ ```js
43
+ connectFunctionsEmulator(functions, 'localhost', 5001);
44
+ ```
45
+
46
+ ### Run The Application Now
47
+
48
+ To run the backend server, inside the `backend/functions` dir run the below command:
49
+
50
+ ```bash
51
+ npm run serve
52
+ ```
53
+
54
+ To run the frontend app, inside the `webapp` dir run the below command:
55
+
56
+ ```bash
57
+ yarn dev
58
+ ```
59
+
60
+ ### Known Errors
61
+
62
+ 1. If you encounter 'npm ERR! /bin/sh: pkg-config: command not found' error in Mac, run the command `brew install pkg-config cairo libpng jpeg giflib pango librsvg`
63
+
64
+ ## Best practices
65
+
66
+ ### Directory structure
67
+
68
+ There are three folders:
69
+ 1. `webapp` is the frontend project of `SceneX`, knowledge requirements:
70
+ - Vue 3
71
+ - Quasar
72
+ - ...
73
+
74
+ 2. `backend` contains source code of backend logic, knowledge requirements:
75
+ - Nodejs
76
+ - Firebase
77
+ - ...
78
+
79
+ 3. `scripts` folder includes custom scripts we might need during the development or for production, currently we have the following scripts:
80
+ - `translate` is responsible for translating and updating our i18n language files in frontend project.
81
+
82
+ ### Best practices of frontend
83
+ 1. **Quasar docs** is your `best friend`. Since the frontend project highly depends on framework `Quasar`. It is recommended to use the predefined classes and components and avoid defining your custom classes
84
+ 2. **Double check** of the UI output in `Dark mode` and `Light mode`. Again, use predefined classes and props.
85
+ 3. **Plugins in boot** folder: create corresponding file in `boot` folder and use them in `quasar.config.js`:
86
+ ```js
87
+ module.exports = configure(function() {
88
+ return {
89
+ ...
90
+ boot: [
91
+ 'i18n',
92
+ 'axios',
93
+ 'firebase',
94
+ 'addressbar-color',
95
+ 'quasar-lang-pack'
96
+ ],
97
+ ...
98
+ }
99
+ })
100
+
101
+ ```
102
+
103
+ ### Best practices of backend
104
+ 1. **Remember to deploy your functions** by running:
105
+ ```bash
106
+ # deploy all functions
107
+ firebase deploy --only functions
108
+
109
+ # deploy a specific function
110
+ firebase deploy --only functions:{function name}
111
+
112
+ ```
backend/.firebaserc ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "projects": {
3
+ "default": "reader-6b7dc"
4
+ }
5
+ }
backend/.gitignore ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ firebase-debug.log*
8
+ firebase-debug.*.log*
9
+
10
+ # Firebase cache
11
+ .firebase/
12
+
13
+ # Firebase config
14
+
15
+ # Uncomment this if you'd like others to create their own Firebase project.
16
+ # For a team working on the same Firebase project(s), it is recommended to leave
17
+ # it commented so all members can deploy to the same project(s) in .firebaserc.
18
+ # .firebaserc
19
+
20
+ # Runtime data
21
+ pids
22
+ *.pid
23
+ *.seed
24
+ *.pid.lock
25
+
26
+ # Directory for instrumented libs generated by jscoverage/JSCover
27
+ lib-cov
28
+
29
+ # Coverage directory used by tools like istanbul
30
+ coverage
31
+
32
+ # nyc test coverage
33
+ .nyc_output
34
+
35
+ # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
36
+ .grunt
37
+
38
+ # Bower dependency directory (https://bower.io/)
39
+ bower_components
40
+
41
+ # node-waf configuration
42
+ .lock-wscript
43
+
44
+ # Compiled binary addons (http://nodejs.org/api/addons.html)
45
+ build/Release
46
+
47
+ # Dependency directories
48
+ node_modules/
49
+
50
+ # Optional npm cache directory
51
+ .npm
52
+
53
+ # Optional eslint cache
54
+ .eslintcache
55
+
56
+ # Optional REPL history
57
+ .node_repl_history
58
+
59
+ # Output of 'npm pack'
60
+ *.tgz
61
+
62
+ # Yarn Integrity file
63
+ .yarn-integrity
64
+
65
+ # dotenv environment variables file
66
+ .env
67
+ .secret.local
68
+
69
+ toy*.ts
70
+
71
+ .DS_Store
72
+ build/
73
+ .firebase-emu/
74
+ *.log
75
+ .DS_Store
backend/firebase.json ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "firestore": {
3
+ "rules": "firestore.rules",
4
+ "indexes": "firestore.indexes.json"
5
+ },
6
+ "functions": [
7
+ {
8
+ "source": "functions",
9
+ "codebase": "default",
10
+ "ignore": [
11
+ "node_modules",
12
+ "src",
13
+ ".git",
14
+ "firebase-debug.log",
15
+ "firebase-debug.*.log"
16
+ ],
17
+ "predeploy": [
18
+ "npm --prefix \"$RESOURCE_DIR\" run build:clean",
19
+ "npm --prefix \"$RESOURCE_DIR\" run build"
20
+ ]
21
+ }
22
+ ],
23
+ "storage": {
24
+ "rules": "storage.rules"
25
+ },
26
+ "emulators": {
27
+ "ui": {
28
+ "enabled": true
29
+ },
30
+ "singleProjectMode": true,
31
+ "functions": {
32
+ "port": 5001
33
+ },
34
+ "auth": {
35
+ "port": 9099
36
+ },
37
+ "firestore": {
38
+ "port": 9098
39
+ },
40
+ "storage": {
41
+ "port": 9097
42
+ }
43
+ }
44
+ }
backend/firestore.indexes.json ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "indexes": [
3
+ {
4
+ "collectionGroup": "prompts",
5
+ "queryScope": "COLLECTION_GROUP",
6
+ "fields": [
7
+ {
8
+ "fieldPath": "id",
9
+ "order": "ASCENDING"
10
+ },
11
+ {
12
+ "fieldPath": "isPublic",
13
+ "order": "ASCENDING"
14
+ }
15
+ ]
16
+ }
17
+ ],
18
+ "fieldOverrides": []
19
+ }
backend/firestore.rules ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ rules_version = '2';
2
+ service cloud.firestore {
3
+ match /databases/{database}/documents {
4
+ // match /questions/{document=**} {
5
+ // allow read: if request.auth != null
6
+ // }
7
+
8
+ // match /answers/{userId}/profiles/default {
9
+ // allow read, write: if request.auth != null && request.auth.uid == userId
10
+ // }
11
+
12
+ match /credits/{userId}/{document=**} {
13
+ allow read: if request.auth != null && request.auth.uid == userId
14
+ }
15
+
16
+ match /users/{userId}/prompts/{document=**} {
17
+ allow read: if request.auth != null && request.auth.uid == userId
18
+ }
19
+
20
+ // match /users/{userId}/profiles/{document=**} {
21
+ // allow read: if request.auth != null && request.auth.uid == userId
22
+ // }
23
+
24
+ match /users/{userId}/creditHistory/{document=**} {
25
+ allow read: if request.auth != null && request.auth.uid == userId
26
+ }
27
+
28
+ match /{document=**} {
29
+ allow read, write: if false;
30
+ }
31
+ }
32
+ }
backend/functions/.editorconfig ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ root = true
2
+
3
+ [*]
4
+ end_of_line = lf
5
+ charset = utf-8
6
+ indent_style = space
7
+ insert_final_newline = true
8
+ trim_trailing_whitespace = true
9
+ indent_size = 4
10
+ quote_type = single
11
+ max_line_length = 120
12
+
13
+ [*.py]
14
+ indent_size = 4
15
+
16
+ [*.ts]
17
+ indent_size = 4
18
+
19
+ [*.js]
20
+ indent_size = 2
21
+
22
+ [*.vue]
23
+ indent_size = 2
24
+
25
+ [*.*sx]
26
+ indent_size = 2
27
+
28
+ [*.*ml]
29
+ indent_size = 2
30
+
31
+ [*.json]
32
+ indent_size = 2
33
+
34
+ [*.md]
35
+ indent_size = 2
36
+ trim_trailing_whitespace = false
backend/functions/.env.example ADDED
File without changes
backend/functions/.vscode/launch.json ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ // 使用 IntelliSense 了解相关属性。
3
+ // 悬停以查看现有属性的描述。
4
+ // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
5
+ "version": "0.2.0",
6
+ "configurations": [
7
+ {
8
+ "name": "Attach by Process ID",
9
+ "processId": "${command:PickProcess}",
10
+ "request": "attach",
11
+ "skipFiles": [
12
+ "<node_internals>/**"
13
+ ],
14
+ "type": "node"
15
+ },
16
+ {
17
+ "name": "Attach",
18
+ "port": 9229,
19
+ "request": "attach",
20
+ "skipFiles": [
21
+ "<node_internals>/**"
22
+ ],
23
+ "type": "node"
24
+ }
25
+ ]
26
+ }
27
+
backend/functions/.vscode/settings.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cSpell.words": [
3
+ "AIHTTP",
4
+ "Castable",
5
+ "civkit",
6
+ "Firestore",
7
+ "openai"
8
+ ],
9
+ "typescript.tsdk": "node_modules/typescript/lib"
10
+ }
backend/functions/firebase-export-1712748362961bSfwZx/firestore_export/firestore_export.overall_export_metadata ADDED
Binary file (15 Bytes). View file
 
backend/functions/package.json ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "url2text",
3
+ "scripts": {
4
+ "lint": "eslint --ext .js,.ts .",
5
+ "build": "tsc -p .",
6
+ "build:watch": "tsc --watch",
7
+ "build:clean": "rm -rf ./build",
8
+ "shell": "npm run build && firebase functions:shell",
9
+ "emu:stage": "cd .. && tar -czvf firebase-emu-preset.tgz .firebase-emu",
10
+ "emu:reset": "rm -rf ../.firebase-emu && tar -xzf ../firebase-emu-preset.tgz --directory ../",
11
+ "emu:start": "firebase emulators:start --import ../.firebase-emu --export-on-exit",
12
+ "emu:debug": "firebase emulators:start --import ../.firebase-emu --export-on-exit --inspect-functions",
13
+ "emu:debug2": "firebase emulators:start --import ../.firebase-emu --export-on-exit --inspect-functions",
14
+ "emu:kill": "killall java",
15
+ "serve": "npm run build && npm run emu:start",
16
+ "debug": "npm run build && npm run emu:start -- --inspect-functions",
17
+ "from-scratch": "npm run build && rm -rf ../.firebase-emu && firebase emulators:start --export-on-exit",
18
+ "from-preset": "npm run build && npm run emu:reset && npm run emu:start",
19
+ "start": "npm run shell",
20
+ "deploy": "firebase deploy --only functions",
21
+ "logs": "firebase functions:log"
22
+ },
23
+ "engines": {
24
+ "node": "20"
25
+ },
26
+ "main": "build/index.js",
27
+ "dependencies": {
28
+ "@google-cloud/translate": "^8.2.0",
29
+ "@mozilla/readability": "^0.5.0",
30
+ "@napi-rs/canvas": "^0.1.44",
31
+ "@types/turndown": "^5.0.4",
32
+ "archiver": "^6.0.1",
33
+ "axios": "^1.3.3",
34
+ "bcrypt": "^5.1.0",
35
+ "civkit": "^0.6.5-be430ac",
36
+ "cors": "^2.8.5",
37
+ "dayjs": "^1.11.9",
38
+ "express": "^4.19.2",
39
+ "firebase-admin": "^11.5.0",
40
+ "firebase-functions": "^4.8.0",
41
+ "generic-pool": "^3.9.0",
42
+ "htmlparser2": "^9.0.0",
43
+ "jose": "^5.1.0",
44
+ "langdetect": "^0.2.1",
45
+ "minio": "^7.1.3",
46
+ "openai": "^4.20.0",
47
+ "puppeteer": "^22.6.3",
48
+ "stripe": "^11.11.0",
49
+ "tiktoken": "^1.0.10",
50
+ "turndown": "^7.1.3",
51
+ "undici": "^5.24.0"
52
+ },
53
+ "devDependencies": {
54
+ "@types/archiver": "^5.3.4",
55
+ "@types/bcrypt": "^5.0.0",
56
+ "@types/cors": "^2.8.17",
57
+ "@types/generic-pool": "^3.8.1",
58
+ "@types/node": "^18",
59
+ "@typescript-eslint/eslint-plugin": "^5.12.0",
60
+ "@typescript-eslint/parser": "^5.12.0",
61
+ "eslint": "^8.9.0",
62
+ "eslint-config-google": "^0.14.0",
63
+ "eslint-plugin-import": "^2.25.4",
64
+ "firebase-functions-test": "^3.0.0",
65
+ "replicate": "^0.16.1",
66
+ "typescript": "^5.1.6"
67
+ },
68
+ "private": true,
69
+ "exports": {
70
+ ".": "./build/index.js"
71
+ }
72
+ }
backend/functions/src/cloud-functions/crawler.ts ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { marshalErrorLike, RPCHost, RPCReflection } from 'civkit';
2
+ import { singleton } from 'tsyringe';
3
+ import { CloudHTTPv2, Logger, OutputServerEventStream, Param, RPCReflect } from '../shared';
4
+ import _ from 'lodash';
5
+ import { PuppeteerControl } from '../services/puppeteer';
6
+ import TurnDownService from 'turndown';
7
+
8
+
9
+ @singleton()
10
+ export class CrawlerHost extends RPCHost {
11
+ logger = this.globalLogger.child({ service: this.constructor.name });
12
+
13
+ turnDownService = new TurnDownService();
14
+
15
+ constructor(
16
+ protected globalLogger: Logger,
17
+ protected puppeteerControl: PuppeteerControl,
18
+ ) {
19
+ super(...arguments);
20
+ }
21
+
22
+ override async init() {
23
+ await this.dependencyReady();
24
+
25
+ this.emit('ready');
26
+ }
27
+
28
+ @CloudHTTPv2({
29
+ exportInGroup: ['crawler'],
30
+ httpMethod: ['get', 'post'],
31
+ returnType: OutputServerEventStream,
32
+ })
33
+ async crawl(
34
+ @RPCReflect() rpcReflect: RPCReflection,
35
+ @Param('url', { required: true }) url: string
36
+ ) {
37
+ await this.serviceReady();
38
+ const sseStream = new OutputServerEventStream();
39
+
40
+ rpcReflect.return(sseStream);
41
+
42
+ try {
43
+ for await (const scrapped of this.puppeteerControl.scrap(url)) {
44
+ this.logger.info(`Scrapped: ${scrapped.snapshot}`);
45
+ const content = typeof scrapped.snapshot === 'string' ? scrapped.snapshot : (scrapped.snapshot as any)?.content;
46
+ if (!content) {
47
+ continue;
48
+ }
49
+ const text = this.turnDownService.turndown(typeof scrapped.snapshot === 'string' ? scrapped.snapshot : (scrapped.snapshot as any)?.content);
50
+ sseStream.write({
51
+ event: 'data',
52
+ data: text,
53
+ });
54
+ }
55
+ } catch (err: any) {
56
+ this.logger.error(`Failed to crawl ${url}`, { err: marshalErrorLike(err) });
57
+ sseStream.write({
58
+ event: 'error',
59
+ data: err,
60
+ });
61
+ }
62
+
63
+ sseStream.end();
64
+
65
+ return sseStream;
66
+ }
67
+
68
+
69
+ }
backend/functions/src/fetch.d.ts ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ declare global {
2
+ export const {
3
+ fetch,
4
+ FormData,
5
+ Headers,
6
+ Request,
7
+ Response,
8
+ File,
9
+ }: typeof import('undici');
10
+ export type { FormData, Headers, Request, RequestInit, Response, RequestInit, File } from 'undici';
11
+ }
12
+
13
+ export { };
backend/functions/src/index.ts ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import 'reflect-metadata';
2
+ import * as functions from 'firebase-functions';
3
+ import { initializeApp } from 'firebase-admin/app';
4
+ initializeApp();
5
+
6
+ import secretExposer from './shared/services/secrets';
7
+
8
+ export const onUserCreated = functions
9
+ .runWith({ secrets: [...secretExposer.bundle], memory: '512MB' })
10
+ .auth.user()
11
+ .onCreate(async (user) => {
12
+
13
+ return null;
14
+ });
15
+
16
+ export const onUserLogin = functions
17
+ .runWith({ secrets: [...secretExposer.bundle], memory: '512MB' })
18
+ .auth.user()
19
+ .beforeSignIn(async (user, _ctx) => {
20
+
21
+ return;
22
+ });
23
+
24
+ import { loadModulesDynamically, registry } from './shared';
25
+ import path from 'path';
26
+ loadModulesDynamically(path.resolve(__dirname, 'cloud-functions'));
27
+
28
+ Object.assign(exports, registry.exportGrouped({
29
+ memory: '1GiB',
30
+ timeoutSeconds: 540,
31
+ }));
32
+ registry.title = 'url2text';
33
+ registry.version = '0.1.0';
backend/functions/src/services/puppeteer.ts ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { AsyncService, Defer } from 'civkit';
2
+ import { container, singleton } from 'tsyringe';
3
+ import puppeteer, { Browser } from 'puppeteer';
4
+ import { Logger } from '../shared/services/logger';
5
+ import genericPool from 'generic-pool';
6
+ import os from 'os';
7
+ import fs from 'fs';
8
+
9
+
10
+ const READABILITY_JS = fs.readFileSync(require.resolve('@mozilla/readability/Readability.js'), 'utf-8');
11
+
12
+ @singleton()
13
+ export class PuppeteerControl extends AsyncService {
14
+
15
+ browser!: Browser;
16
+ logger = this.globalLogger.child({ service: this.constructor.name });
17
+
18
+ pagePool = genericPool.createPool({
19
+ create: async () => {
20
+ const page = await this.newPage();
21
+ return page;
22
+ },
23
+ destroy: async (page) => {
24
+ await page.browserContext().close();
25
+ },
26
+ validate: async (page) => {
27
+ return this.browser.connected && !page.isClosed();
28
+ }
29
+ }, {
30
+ max: Math.ceil(os.freemem() / 1024 * 1024 * 1024),
31
+ min: 0,
32
+ });
33
+
34
+ constructor(protected globalLogger: Logger) {
35
+ super(...arguments);
36
+ }
37
+
38
+ override async init() {
39
+ await this.dependencyReady();
40
+
41
+ if (this.browser) {
42
+ await this.browser.close();
43
+ }
44
+ this.browser = await puppeteer.launch({
45
+ headless: false,
46
+ args: ['--no-sandbox', '--disable-setuid-sandbox'],
47
+ });
48
+ this.browser.once('disconnected', () => {
49
+ this.logger.warn(`Browser disconnected`);
50
+ this.emit('crippled');
51
+ });
52
+
53
+ this.emit('ready');
54
+ }
55
+
56
+ async newPage() {
57
+ await this.serviceReady();
58
+ const dedicatedContext = await this.browser.createBrowserContext();
59
+
60
+ const page = await dedicatedContext.newPage();
61
+ await page.setUserAgent(`Slackbot-LinkExpanding 1.0 (+https://api.slack.com/robots)`);
62
+ await page.setViewport({ width: 1920, height: 1080 });
63
+ await page.exposeFunction('reportSnapshot', (snapshot: any) => {
64
+ page.emit('snapshot', snapshot);
65
+ });
66
+
67
+ await page.evaluateOnNewDocument(READABILITY_JS);
68
+
69
+ await page.evaluateOnNewDocument(() => {
70
+ // @ts-expect-error
71
+ window.giveSnapshot() = () => {
72
+ // @ts-expect-error
73
+ return new Readability(document.cloneNode(true)).parse();
74
+ };
75
+ let aftershot: any;
76
+ const handlePageLoad = () => {
77
+ // @ts-expect-error
78
+ if (document.readyState !== 'complete' && document.readyState !== 'interactive') {
79
+ return;
80
+ }
81
+
82
+ // @ts-expect-error
83
+ const parsed = window.giveSnapshot();
84
+ console.log(parsed);
85
+ if (parsed) {
86
+ // @ts-expect-error
87
+ window.reportSnapshot(parsed);
88
+ } else {
89
+ if (aftershot) {
90
+ clearTimeout(aftershot);
91
+ }
92
+ aftershot = setTimeout(() => {
93
+ // @ts-expect-error
94
+ window.reportSnapshot(window.giveSnapshot());
95
+ }, 500);
96
+ }
97
+ };
98
+ // setInterval(handlePageLoad, 1000);
99
+ // @ts-expect-error
100
+ document.addEventListener('readystatechange', handlePageLoad);
101
+ // @ts-expect-error
102
+ document.addEventListener('load', handlePageLoad);
103
+ });
104
+
105
+ // TODO: further setup the page;
106
+
107
+ return page;
108
+ }
109
+
110
+ async *scrap(url: string) {
111
+ const page = await this.pagePool.acquire();
112
+ let snapshot: unknown;
113
+ let nextSnapshotDeferred = Defer();
114
+ let finalized = false;
115
+ const hdl = (s: any) => {
116
+ if (snapshot === s) {
117
+ return;
118
+ }
119
+ snapshot = s;
120
+ nextSnapshotDeferred.resolve(s);
121
+ nextSnapshotDeferred = Defer();
122
+ };
123
+ page.on('snapshot', hdl);
124
+ const gotoPromise = page.goto(url, { waitUntil: 'networkidle2', timeout: 30_000 });
125
+ gotoPromise.finally(() => finalized = true);
126
+
127
+ try {
128
+ while (true) {
129
+ await Promise.race([nextSnapshotDeferred.promise, gotoPromise]);
130
+ const screenshot = await page.screenshot();
131
+ if (finalized) {
132
+ await gotoPromise;
133
+ snapshot = await page.evaluate('window.giveSnapshot()');
134
+ yield { snapshot, screenshot };
135
+ break;
136
+ }
137
+ yield { snapshot, screenshot };
138
+ }
139
+ } catch (_err) {
140
+ void 0;
141
+ } finally {
142
+ page.off('snapshot', hdl);
143
+ await this.pagePool.destroy(page);
144
+ }
145
+
146
+ }
147
+
148
+ }
149
+
150
+ const puppeteerControl = container.resolve(PuppeteerControl);
151
+
152
+ export default puppeteerControl;
backend/functions/src/shared ADDED
@@ -0,0 +1 @@
 
 
1
+ ../../../thinapps-shared/backend
backend/functions/src/types.d.ts ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ declare module 'langdetect' {
2
+ interface DetectionResult {
3
+ lang: string;
4
+ prob: number;
5
+ }
6
+
7
+ export function detect(text: string): DetectionResult[];
8
+ export function detectOne(text: string): string | null;
9
+ }
backend/functions/tsconfig.json ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "noImplicitReturns": true,
5
+ "noUnusedLocals": true,
6
+ "outDir": "build",
7
+ "sourceMap": true,
8
+ "strict": true,
9
+ "allowJs": true,
10
+ "target": "es2022",
11
+ "lib": ["es2022"],
12
+ "skipLibCheck": true,
13
+ "useDefineForClassFields": false,
14
+ "experimentalDecorators": true,
15
+ "emitDecoratorMetadata": true,
16
+ "esModuleInterop": true,
17
+ "noImplicitOverride": true,
18
+ },
19
+ "compileOnSave": true,
20
+ "include": ["src"]
21
+ }
backend/storage.rules ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ rules_version = '2';
2
+ service firebase.storage {
3
+ match /b/{bucket}/o {
4
+ match /{allPaths=**} {
5
+ allow read, write: if false;
6
+ }
7
+ }
8
+ }
package.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "url2text",
3
+ "version": "1.0.0",
4
+ "description": "### Prerequisite - Node v18 (The build fails for Node version >18) - Yarn - Firebase CLI (`npm install -g firebase-tools`)",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "author": "",
10
+ "license": "ISC",
11
+ "devDependencies": {
12
+ "firebase-tools": "^12.4.2",
13
+ "typescript": "^5.1.6"
14
+ }
15
+ }
thinapps-shared ADDED
@@ -0,0 +1 @@
 
 
1
+ Subproject commit 9f0fa1dd7f8cfcea4c8d79252319b151fae6ed19