├── .gitignore
├── Cargo.lock
├── LICENSE.md
├── README.md
├── cargo.toml
├── index.html
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
└── flags
│ ├── ad.svg
│ ├── ae.svg
│ ├── af.svg
│ ├── ag.svg
│ ├── ai.svg
│ ├── al.svg
│ ├── am.svg
│ ├── ao.svg
│ ├── ar.svg
│ ├── as.svg
│ ├── at.svg
│ ├── au.svg
│ ├── aw.svg
│ ├── ax.svg
│ ├── az.svg
│ ├── ba.svg
│ ├── bb.svg
│ ├── bd.svg
│ ├── be.svg
│ ├── bf.svg
│ ├── bg.svg
│ ├── bh.svg
│ ├── bi.svg
│ ├── bj.svg
│ ├── bl.svg
│ ├── bm.svg
│ ├── bn.svg
│ ├── bo.svg
│ ├── bq.svg
│ ├── br.svg
│ ├── bs.svg
│ ├── bt.svg
│ ├── bv.svg
│ ├── bw.svg
│ ├── by.svg
│ ├── bz.svg
│ ├── ca.svg
│ ├── cc.svg
│ ├── cd.svg
│ ├── cf.svg
│ ├── cg.svg
│ ├── ch.svg
│ ├── ci.svg
│ ├── ck.svg
│ ├── cl.svg
│ ├── cm.svg
│ ├── cn.svg
│ ├── co.svg
│ ├── cr.svg
│ ├── cu.svg
│ ├── cv.svg
│ ├── cw.svg
│ ├── cx.svg
│ ├── cy.svg
│ ├── cz.svg
│ ├── de.svg
│ ├── dj.svg
│ ├── dk.svg
│ ├── dm.svg
│ ├── do.svg
│ ├── dz.svg
│ ├── ec.svg
│ ├── ee.svg
│ ├── eg.svg
│ ├── er.svg
│ ├── es.svg
│ ├── et.svg
│ ├── eu.svg
│ ├── fi.svg
│ ├── fj.svg
│ ├── fk.svg
│ ├── fm.svg
│ ├── fo.svg
│ ├── fr.svg
│ ├── ga.svg
│ ├── gb.svg
│ ├── gd.svg
│ ├── ge.svg
│ ├── gf.svg
│ ├── gg.svg
│ ├── gh.svg
│ ├── gi.svg
│ ├── gl.svg
│ ├── gm.svg
│ ├── gn.svg
│ ├── gp.svg
│ ├── gq.svg
│ ├── gr.svg
│ ├── gt.svg
│ ├── gu.svg
│ ├── gw.svg
│ ├── gy.svg
│ ├── hk.svg
│ ├── hn.svg
│ ├── hr.svg
│ ├── ht.svg
│ ├── hu.svg
│ ├── id.svg
│ ├── ie.svg
│ ├── il.svg
│ ├── im.svg
│ ├── in.svg
│ ├── iq.svg
│ ├── ir.svg
│ ├── is.svg
│ ├── it.svg
│ ├── je.svg
│ ├── jm.svg
│ ├── jo.svg
│ ├── jp.svg
│ ├── ke.svg
│ ├── kg.svg
│ ├── kh.svg
│ ├── ki.svg
│ ├── km.svg
│ ├── kn.svg
│ ├── kr.svg
│ ├── kw.svg
│ ├── ky.svg
│ ├── kz.svg
│ ├── la.svg
│ ├── lb.svg
│ ├── lc.svg
│ ├── li.svg
│ ├── lk.svg
│ ├── lr.svg
│ ├── ls.svg
│ ├── lt.svg
│ ├── lu.svg
│ ├── lv.svg
│ ├── ly.svg
│ ├── ma.svg
│ ├── mc.svg
│ ├── md.svg
│ ├── me.svg
│ ├── mf.svg
│ ├── mg.svg
│ ├── mh.svg
│ ├── mk.svg
│ ├── ml.svg
│ ├── mm.svg
│ ├── mn.svg
│ ├── mo.svg
│ ├── mp.svg
│ ├── mq.svg
│ ├── mr.svg
│ ├── ms.svg
│ ├── mt.svg
│ ├── mu.svg
│ ├── mv.svg
│ ├── mw.svg
│ ├── mx.svg
│ ├── my.svg
│ ├── mz.svg
│ ├── na.svg
│ ├── nc.svg
│ ├── ne.svg
│ ├── nf.svg
│ ├── ng.svg
│ ├── ni.svg
│ ├── nl.svg
│ ├── no.svg
│ ├── np.svg
│ ├── nr.svg
│ ├── nu.svg
│ ├── nz.svg
│ ├── om.svg
│ ├── pa.svg
│ ├── pe.svg
│ ├── pf.svg
│ ├── pg.svg
│ ├── ph.svg
│ ├── pk.svg
│ ├── pl.svg
│ ├── pm.svg
│ ├── pn.svg
│ ├── pr.svg
│ ├── ps.svg
│ ├── pt.svg
│ ├── pw.svg
│ ├── py.svg
│ ├── qa.svg
│ ├── re.svg
│ ├── ro.svg
│ ├── rs.svg
│ ├── ru.svg
│ ├── rw.svg
│ ├── sa.svg
│ ├── sb.svg
│ ├── sc.svg
│ ├── sd.svg
│ ├── se.svg
│ ├── sg.svg
│ ├── si.svg
│ ├── sj.svg
│ ├── sk.svg
│ ├── sl.svg
│ ├── sm.svg
│ ├── sn.svg
│ ├── so.svg
│ ├── sr.svg
│ ├── st.svg
│ ├── sv.svg
│ ├── sx.svg
│ ├── sy.svg
│ ├── sz.svg
│ ├── tc.svg
│ ├── td.svg
│ ├── tg.svg
│ ├── th.svg
│ ├── tj.svg
│ ├── tk.svg
│ ├── tl.svg
│ ├── tm.svg
│ ├── tn.svg
│ ├── to.svg
│ ├── tr.svg
│ ├── tt.svg
│ ├── tv.svg
│ ├── tw.svg
│ ├── tz.svg
│ ├── ua.svg
│ ├── ug.svg
│ ├── us.svg
│ ├── uy.svg
│ ├── uz.svg
│ ├── vc.svg
│ ├── ve.svg
│ ├── vg.svg
│ ├── vi.svg
│ ├── vn.svg
│ ├── vu.svg
│ ├── wf.svg
│ ├── ws.svg
│ ├── xk.svg
│ ├── ye.svg
│ ├── yt.svg
│ ├── za.svg
│ ├── zm.svg
│ └── zw.svg
├── server
├── Cargo.toml
├── diesel.toml
├── migrations
│ ├── 00000000000000_diesel_initial_setup
│ │ ├── down.sql
│ │ └── up.sql
│ ├── 2022-11-27-151554_users
│ │ ├── down.sql
│ │ └── up.sql
│ └── 2022-11-27-160701_sessions
│ │ ├── down.sql
│ │ └── up.sql
└── src
│ ├── api.rs
│ ├── database.rs
│ ├── main.rs
│ ├── middlewares
│ ├── mod.rs
│ └── session.rs
│ ├── models.rs
│ ├── models
│ └── osu.rs
│ ├── routes
│ ├── auth
│ │ ├── authorize.rs
│ │ ├── login.rs
│ │ ├── mod.rs
│ │ └── refresh.rs
│ ├── mod.rs
│ └── mutuals.rs
│ ├── schema.rs
│ └── utils.rs
├── src-tauri
├── Cargo.toml
├── build.rs
├── icons
│ ├── 128x128.png
│ ├── 128x128@2x.png
│ ├── icon.icns
│ └── icon.ico
├── src
│ └── main.rs
└── tauri.conf.json
├── src
├── App.vue
├── animation.ts
├── api
│ ├── auth.ts
│ ├── cookies.ts
│ ├── friends.ts
│ └── user.ts
├── assets
│ └── icon.ico
├── components
│ ├── AppList.vue
│ ├── AppSide.vue
│ ├── AppTitleBar.vue
│ ├── AppVersion.vue
│ ├── DevRouter.vue
│ ├── Icons
│ │ ├── Back.vue
│ │ ├── Clear.vue
│ │ ├── Close.vue
│ │ ├── Login.vue
│ │ ├── Maximize.vue
│ │ ├── Minimize.vue
│ │ ├── Settings.vue
│ │ ├── Spinner.vue
│ │ └── UnMaximize.vue
│ ├── Login
│ │ └── LoginMutuals.vue
│ ├── Settings
│ │ ├── SettingsBase.vue
│ │ ├── SettingsBlacklist.vue
│ │ ├── SettingsCountries.vue
│ │ ├── SettingsCountriesCountry.vue
│ │ ├── SettingsCountriesGlobal.vue
│ │ ├── SettingsDelay.vue
│ │ ├── SettingsMode.vue
│ │ ├── SettingsOther.vue
│ │ ├── SettingsPageLimit.vue
│ │ └── SettingsToggle.vue
│ ├── Ui
│ │ ├── BaseButton.vue
│ │ ├── BaseButtonIcon.vue
│ │ ├── BaseCheckbox.vue
│ │ ├── BaseInput.vue
│ │ ├── BaseInputNumber.vue
│ │ ├── BaseRadio.vue
│ │ └── BaseSuspense.vue
│ └── User.vue
├── env.d.ts
├── index.css
├── main.ts
├── plugin
│ ├── Notification.vue
│ └── notification.ts
├── router
│ └── index.ts
├── store
│ ├── auth.ts
│ ├── index.ts
│ ├── settings.ts
│ └── user.ts
├── types.ts
├── utils.ts
└── views
│ ├── Login.vue
│ ├── Mutuals.vue
│ ├── Settings.vue
│ └── Verify.vue
├── tailwind.config.js
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | /target/
4 | WixTools
5 |
6 | node_modules
7 | *.js
8 | yarn.lock
9 | .yarn
10 | dist
11 | build
12 | electron-builder.env
13 | *.env.*
14 | .dev.env.*
15 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 metkm
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 | # Important
2 | 
3 |
4 | This is the reason why the app is broken right now. So you can assume the app will be broken from now on.
5 |
6 | ----------------------------------------------------------------------------------------------------
7 |
8 |
9 | Be sure to add countries to check from settings. You can double click to country name to add it.
10 |
11 | It doesn't store your passwords in any way. If you are still can't trust it what you can do is since it's already open source you can read the code and build the app yourself.
12 |
13 | # Some screenshots!
14 |
15 | 
16 | 
17 |
18 | # Running the project for development
19 | If you are looking for releases to just use the program go to releases https://github.com/metkm/osu-mutual-finder/releases
20 |
21 | ### For the app
22 | `.env.development`
23 | ```
24 | VITE_API_BASE_URL=http://localhost:3001
25 | ```
26 | ```
27 | npm run tauri dev
28 | ```
29 | ### For the server
30 | database setup
31 | https://diesel.rs/guides/getting-started
32 |
33 | `.env.development`
34 | ```
35 | DATABASE_URL=postgres://user:password@localhost:5432/mutualfinder
36 | CLIENT_ID=123
37 | CLIENT_SECRET=123
38 | AUTH_REDIRECT_URI=http://localhost:3001/api/authorize
39 | REDIRECT_URI=http://localhost:3001
40 | ```
41 |
--------------------------------------------------------------------------------
/cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 |
3 | members = [
4 | "src-tauri",
5 | "server"
6 | ]
7 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Mutual Finder
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "osu-mutual-finder",
3 | "version": "1.4.5",
4 | "private": true,
5 | "author": "Sibyl",
6 | "description": "Easy way to find who added you in osu!",
7 | "license": "MIT",
8 | "scripts": {
9 | "dev": "vite",
10 | "build": "vue-tsc --noEmit && vite build",
11 | "preview": "vite preview",
12 | "tauri": "tauri"
13 | },
14 | "dependencies": {
15 | "@tauri-apps/api": "^1.2.0",
16 | "ky": "^0.33.2",
17 | "mitt": "^3.0.0",
18 | "pinia": "^2.0.28",
19 | "pinia-plugin-persistedstate": "^2.4.0"
20 | },
21 | "devDependencies": {
22 | "@tauri-apps/cli": "^1.2.3",
23 | "@types/node": "^18.7.10",
24 | "@vitejs/plugin-vue": "^4.0.0",
25 | "autoprefixer": "^10.4.13",
26 | "postcss": "^8.4.21",
27 | "tailwindcss": "^3.2.4",
28 | "typescript": "^4.9.3",
29 | "vite": "^4.0.4",
30 | "vue": "^3.2.45",
31 | "vue-router": "^4.1.6",
32 | "vue-tsc": "^1.0.22"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/flags/ae.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ag.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/am.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ao.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/at.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/au.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/aw.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ax.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/az.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ba.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/bb.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/bd.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/be.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/bf.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/bg.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/bh.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/bi.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/bj.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/bq.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/br.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/bs.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/bv.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/bw.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/by.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ca.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/cc.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/cd.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/cf.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/cg.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ch.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ci.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/cl.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/cm.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/cn.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/co.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/cr.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/cu.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/cv.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/cw.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/cx.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/cz.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/de.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/dj.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/dk.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/dz.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ec.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ee.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/es.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/et.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/eu.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/fi.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/fm.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/fo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/fr.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ga.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/gb.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/gd.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ge.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/gf.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/gg.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/gh.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/gl.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/gm.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/gn.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/gr.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/gw.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/gy.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/hn.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/hr.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/hu.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/id.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ie.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/il.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/im.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/iq.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ir.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/is.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/it.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/jm.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/jo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/jp.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/kh.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/km.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/kn.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/kr.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/kw.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/la.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/lc.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/lr.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ls.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/lt.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/lu.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/lv.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ly.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ma.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/mc.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/md.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/mf.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/mg.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/mh.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/mk.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ml.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/mm.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/mn.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/mr.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/mu.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/mv.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/mw.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/my.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/mz.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/na.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/nc.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ne.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ng.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ni.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/nl.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/no.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/np.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/nr.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/nu.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/nz.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/om.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/pa.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/pe.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ph.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/pk.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/pl.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/pr.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ps.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/pt.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/pw.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/qa.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/re.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ro.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ru.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/rw.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/sb.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/sc.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/sd.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/se.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/sg.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/si.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/sj.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/sk.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/sl.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/sn.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/so.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/sr.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/st.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/sy.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/td.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/tg.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/th.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/tj.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/tk.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/tl.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/tn.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/to.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/tr.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/tt.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/tw.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/tz.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ua.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ug.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/uy.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/uz.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/vc.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ve.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/vn.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/vu.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/wf.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ws.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/ye.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/flags/za.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/server/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "server"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 |
8 | [dependencies]
9 | rand = "0.8.5"
10 | dotenvy = "0.15.1"
11 | reqwest = { version = "0.11.11", features=["json"] }
12 | serde = { version = "1", features=["derive"] }
13 | diesel = { version = "2.0.0", features = ["postgres", "r2d2"] }
14 |
15 | axum = { version = "0.6.0", features = ["macros"] }
16 | axum-extra = { version = "0.4.0", features=["cookie"] }
17 | tower-http = { version = "0.3.4", features=["cors", "trace"] }
18 |
19 | tokio = { version = "1.20.1", features=["full"] }
20 | time = "0.3.12"
21 | tracing = "0.1.37"
22 | tracing-subscriber = "0.3.16"
23 |
--------------------------------------------------------------------------------
/server/diesel.toml:
--------------------------------------------------------------------------------
1 | # For documentation on how to configure this file,
2 | # see https://diesel.rs/guides/configuring-diesel-cli
3 |
4 | [print_schema]
5 | file = "src/schema.rs"
6 |
7 | [migrations_directory]
8 | dir = "migrations"
9 |
--------------------------------------------------------------------------------
/server/migrations/00000000000000_diesel_initial_setup/down.sql:
--------------------------------------------------------------------------------
1 | -- This file was automatically created by Diesel to setup helper functions
2 | -- and other internal bookkeeping. This file is safe to edit, any future
3 | -- changes will be added to existing projects as new migrations.
4 |
5 | DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass);
6 | DROP FUNCTION IF EXISTS diesel_set_updated_at();
7 |
--------------------------------------------------------------------------------
/server/migrations/00000000000000_diesel_initial_setup/up.sql:
--------------------------------------------------------------------------------
1 | -- This file was automatically created by Diesel to setup helper functions
2 | -- and other internal bookkeeping. This file is safe to edit, any future
3 | -- changes will be added to existing projects as new migrations.
4 |
5 |
6 |
7 |
8 | -- Sets up a trigger for the given table to automatically set a column called
9 | -- `updated_at` whenever the row is modified (unless `updated_at` was included
10 | -- in the modified columns)
11 | --
12 | -- # Example
13 | --
14 | -- ```sql
15 | -- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW());
16 | --
17 | -- SELECT diesel_manage_updated_at('users');
18 | -- ```
19 | CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$
20 | BEGIN
21 | EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
22 | FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
23 | END;
24 | $$ LANGUAGE plpgsql;
25 |
26 | CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
27 | BEGIN
28 | IF (
29 | NEW IS DISTINCT FROM OLD AND
30 | NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at
31 | ) THEN
32 | NEW.updated_at := current_timestamp;
33 | END IF;
34 | RETURN NEW;
35 | END;
36 | $$ LANGUAGE plpgsql;
37 |
--------------------------------------------------------------------------------
/server/migrations/2022-11-27-151554_users/down.sql:
--------------------------------------------------------------------------------
1 | -- This file should undo anything in `up.sql`
2 |
3 | DROP TABLE users
4 |
--------------------------------------------------------------------------------
/server/migrations/2022-11-27-151554_users/up.sql:
--------------------------------------------------------------------------------
1 | -- Your SQL goes here
2 |
3 | CREATE TABLE users (
4 | user_id INTEGER UNIQUE PRIMARY KEY,
5 | username TEXT NOT NULL,
6 | global_rank INTEGER NOT NULL,
7 | country_code TEXT NOT NULL,
8 | avatar_url TEXT NOT NULL,
9 | cover_url TEXT NOT NULL
10 | )
11 |
--------------------------------------------------------------------------------
/server/migrations/2022-11-27-160701_sessions/down.sql:
--------------------------------------------------------------------------------
1 | -- This file should undo anything in `up.sql`
2 |
3 | DROP TABLE sessions
4 |
--------------------------------------------------------------------------------
/server/migrations/2022-11-27-160701_sessions/up.sql:
--------------------------------------------------------------------------------
1 | -- Your SQL goes here
2 |
3 | CREATE TABLE sessions (
4 | user_id INTEGER PRIMARY KEY REFERENCES users(user_id),
5 | friend_ids INTEGER[] NOT NULL,
6 | osu_session TEXT NOT NULL,
7 | access_token TEXT NOT NULL,
8 | refresh_token TEXT NOT NULL
9 | )
10 |
--------------------------------------------------------------------------------
/server/src/database.rs:
--------------------------------------------------------------------------------
1 | use dotenvy;
2 |
3 | use diesel::PgConnection;
4 | use diesel::r2d2::{ConnectionManager, Pool};
5 |
6 | pub fn establish_connection_pool() -> Pool> {
7 | let env_file = if cfg!(debug_assertions) {
8 | ".env.development"
9 | } else {
10 | ".env.production"
11 | };
12 |
13 | dotenvy::from_filename(env_file).expect("Couldn't load .env file");
14 |
15 | let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL env varible must be set");
16 |
17 | let connection_manager = ConnectionManager::::new(&database_url);
18 | let pool = Pool::builder()
19 | .build(connection_manager)
20 | .expect("Failed to create a connection pool");
21 |
22 | pool
23 | }
24 |
--------------------------------------------------------------------------------
/server/src/main.rs:
--------------------------------------------------------------------------------
1 | mod middlewares;
2 | mod database;
3 | mod models;
4 | mod schema;
5 | mod routes;
6 | mod utils;
7 | mod api;
8 |
9 | use axum::{Router, middleware};
10 | use axum::routing::{get, patch};
11 | use tower_http::cors::CorsLayer;
12 | use tower_http::trace::{TraceLayer, DefaultOnRequest, DefaultOnResponse};
13 | use tracing::Level;
14 |
15 | use models::AppState;
16 | use std::net::SocketAddr;
17 | use std::sync::Arc;
18 |
19 |
20 | #[tokio::main]
21 | async fn main() {
22 | let cors = CorsLayer::very_permissive().allow_credentials(true);
23 |
24 | let state = Arc::new(AppState::new());
25 | let session_layer = middleware::from_fn_with_state(state.clone(), middlewares::session::session);
26 |
27 | tracing_subscriber::fmt::init();
28 | let trace_layer = TraceLayer::new_for_http()
29 | .on_request(
30 | DefaultOnRequest::new().level(Level::INFO)
31 | )
32 | .on_response(
33 | DefaultOnResponse::new().level(Level::INFO)
34 | );
35 |
36 | let app = Router::new()
37 | .route("/api/mutuals", get(routes::mutuals::get_mutuals))
38 | .route("/api/refresh", patch(routes::auth::refresh))
39 | .route_layer(session_layer)
40 | .route("/api/login", get(routes::auth::login))
41 | .route("/api/authorize", get(routes::auth::authorize))
42 | .with_state(state)
43 | .layer(trace_layer)
44 | .layer(cors);
45 |
46 | axum::Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3001)))
47 | .serve(app.into_make_service())
48 | .await
49 | .unwrap();
50 | }
51 |
--------------------------------------------------------------------------------
/server/src/middlewares/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod session;
--------------------------------------------------------------------------------
/server/src/middlewares/session.rs:
--------------------------------------------------------------------------------
1 | use std::sync::Arc;
2 |
3 | use axum::extract::State;
4 | use axum::http::Request;
5 | use axum::middleware::Next;
6 | use axum::response::Response;
7 | use axum_extra::extract::CookieJar;
8 |
9 | use diesel::prelude::*;
10 | use diesel::{QueryDsl, RunQueryDsl};
11 |
12 | use reqwest::StatusCode;
13 |
14 | use crate::models::*;
15 | use crate::schema::sessions;
16 |
17 | pub async fn session(
18 | State(state): State>,
19 |
20 | mut req: Request,
21 | next: Next,
22 | ) -> Result
23 | where
24 | B: Send + 'static,
25 | {
26 | let jar = CookieJar::from_headers(req.headers());
27 |
28 | let Some(session_string) = jar.get("osu_session") else {
29 | return Err(StatusCode::UNAUTHORIZED)
30 | };
31 |
32 | let connection = &mut state.connection_pool.get().unwrap();
33 | let value = session_string.value().to_string();
34 |
35 | let Ok(session) = sessions::table
36 | .filter(sessions::osu_session.eq(value))
37 | .first::(connection) else {
38 | return Err(StatusCode::UNAUTHORIZED)
39 | };
40 |
41 | req.extensions_mut()
42 | .insert(session);
43 |
44 | Ok(next.run(req).await)
45 | }
46 |
--------------------------------------------------------------------------------
/server/src/models/osu.rs:
--------------------------------------------------------------------------------
1 | use serde::{Deserialize, Serialize};
2 |
3 | use super::User;
4 |
5 | #[derive(Debug, Deserialize, Serialize)]
6 | pub struct Tokens {
7 | pub token_type: String,
8 | pub expires_in: i32,
9 | pub access_token: String,
10 | pub refresh_token: String,
11 | }
12 |
13 | #[derive(Deserialize, Serialize, Clone)]
14 | pub struct Cover {
15 | pub url: String,
16 | }
17 |
18 | #[derive(Deserialize, Serialize, Clone)]
19 | pub struct Statistics {
20 | pub global_rank: Option,
21 | }
22 |
23 | #[derive(Deserialize, Serialize, Clone)]
24 | pub struct OsuUser {
25 | pub avatar_url: String,
26 | pub country_code: String,
27 | pub id: i32,
28 | pub username: String,
29 | pub cover: Cover,
30 | pub statistics: Statistics,
31 | }
32 |
33 | impl From for OsuUser {
34 | fn from(user: User) -> Self {
35 | Self {
36 | avatar_url: user.avatar_url,
37 | country_code: user.country_code,
38 | id: user.user_id,
39 | username: user.username,
40 | cover: Cover {
41 | url: user.cover_url
42 | },
43 | statistics: Statistics {
44 | global_rank: Some(user.global_rank)
45 | },
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/server/src/routes/auth/login.rs:
--------------------------------------------------------------------------------
1 | use std::sync::Arc;
2 |
3 | use axum::extract::State;
4 | use axum::response::{Redirect, IntoResponse};
5 | use reqwest::{StatusCode, Url};
6 |
7 | use crate::models::AppState;
8 |
9 | pub async fn login(
10 | State(server_state): State>
11 | ) -> Result {
12 | let params = vec![
13 | ("scope", "friends.read public"),
14 | ("client_id", &server_state.client_id),
15 | ("response_type", "code"),
16 | ("redirect_uri", &server_state.auth_redirect_uri)
17 | ];
18 |
19 | let Ok(url) = Url::parse_with_params("https://osu.ppy.sh/oauth/authorize", ¶ms) else {
20 | return Err((StatusCode::INTERNAL_SERVER_ERROR, "Can't parse auth url!"))
21 | };
22 |
23 | let redirect = Redirect::permanent(url.as_str());
24 | Ok(redirect)
25 | }
26 |
--------------------------------------------------------------------------------
/server/src/routes/auth/mod.rs:
--------------------------------------------------------------------------------
1 | mod authorize;
2 | mod refresh;
3 | mod login;
4 |
5 | pub(crate) use authorize::authorize;
6 | pub(crate) use refresh::refresh;
7 | pub(crate) use login::login;
8 |
--------------------------------------------------------------------------------
/server/src/routes/auth/refresh.rs:
--------------------------------------------------------------------------------
1 | use std::{collections::HashMap, sync::Arc};
2 |
3 | use axum::Json;
4 | use axum::{response::IntoResponse, Extension};
5 | use axum::extract::State;
6 | use reqwest::StatusCode;
7 |
8 | use diesel::prelude::*;
9 |
10 | use crate::api::get_tokens;
11 | use crate::models::{Session, AppState};
12 | use crate::schema::sessions;
13 |
14 | pub async fn refresh(
15 | State(state): State>,
16 | Extension(session): Extension
17 | ) -> Result {
18 | let client = reqwest::Client::new();
19 |
20 | let params = HashMap::from([
21 | ("grant_type", "refresh_token"),
22 | ("client_id", &state.client_id),
23 | ("client_secret", &state.client_secret),
24 | ("refresh_token", &session.refresh_token),
25 | ("redirect_uri", &state.auth_redirect_uri)
26 | ]);
27 |
28 | let tokens = get_tokens(&client, ¶ms).await?;
29 | let mut connection = state.connection_pool.get().unwrap();
30 |
31 | let update = diesel::update(
32 | sessions::table.filter(sessions::osu_session.eq(session.osu_session))
33 | )
34 | .set((
35 | sessions::access_token.eq(&tokens.access_token),
36 | sessions::refresh_token.eq(&tokens.refresh_token)
37 | ))
38 | .execute(&mut connection);
39 |
40 | if update.is_err() {
41 | return Err((
42 | StatusCode::INTERNAL_SERVER_ERROR,
43 | "Can't update session tokens"
44 | ))
45 | }
46 |
47 | Ok((StatusCode::OK, Json(tokens)))
48 | }
49 |
--------------------------------------------------------------------------------
/server/src/routes/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod mutuals;
2 | pub mod auth;
3 |
--------------------------------------------------------------------------------
/server/src/routes/mutuals.rs:
--------------------------------------------------------------------------------
1 | use std::sync::Arc;
2 |
3 | use axum::Json;
4 | use axum::response::IntoResponse;
5 | use axum::{Extension, extract::State};
6 | use diesel::PgArrayExpressionMethods;
7 | use diesel::QueryDsl;
8 | use diesel::RunQueryDsl;
9 | use diesel::result::Error;
10 | use diesel::ExpressionMethods;
11 | use reqwest::StatusCode;
12 |
13 | use crate::models::User;
14 | use crate::models::osu::OsuUser;
15 | use crate::models::{Session, AppState};
16 | use crate::schema::sessions;
17 | use crate::schema::users;
18 |
19 |
20 | pub async fn get_mutuals(
21 | State(state): State>,
22 | Extension(session): Extension
23 | ) -> Result {
24 |
25 | let mut connection = state.connection_pool.get().unwrap();
26 | let user_id: Vec = vec![session.user_id];
27 |
28 | let id_query_result: Result, Error> = sessions::table
29 | .select(sessions::user_id)
30 | .filter(sessions::friend_ids.contains(user_id))
31 | .load::(&mut connection);
32 |
33 | let Ok(sessions) = id_query_result else {
34 | return Err((StatusCode::INTERNAL_SERVER_ERROR, "Error while querying friends"))
35 | };
36 |
37 | let friend_query_result = users::table
38 | .filter(users::user_id.eq_any(sessions))
39 | .load::(&mut connection);
40 |
41 | match friend_query_result {
42 | Ok(friends) => {
43 | let osu_v = friends
44 | .into_iter()
45 | .map(OsuUser::from)
46 | .collect();
47 |
48 | return Ok((StatusCode::OK, Json(osu_v)))
49 | },
50 | Err(error) => {
51 | match error {
52 | Error::NotFound => {
53 | return Ok((StatusCode::OK, Json(vec![])))
54 | },
55 | _ => {
56 | return Err((StatusCode::INTERNAL_SERVER_ERROR, "Error while querying users"))
57 | }
58 | }
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/server/src/schema.rs:
--------------------------------------------------------------------------------
1 | // @generated automatically by Diesel CLI.
2 |
3 | diesel::table! {
4 | sessions (user_id) {
5 | user_id -> Int4,
6 | friend_ids -> Array>,
7 | osu_session -> Text,
8 | access_token -> Text,
9 | refresh_token -> Text,
10 | }
11 | }
12 |
13 | diesel::table! {
14 | users (user_id) {
15 | user_id -> Int4,
16 | username -> Text,
17 | global_rank -> Int4,
18 | country_code -> Text,
19 | avatar_url -> Text,
20 | cover_url -> Text,
21 | }
22 | }
23 |
24 | diesel::joinable!(sessions -> users (user_id));
25 |
26 | diesel::allow_tables_to_appear_in_same_query!(
27 | sessions,
28 | users,
29 | );
30 |
--------------------------------------------------------------------------------
/server/src/utils.rs:
--------------------------------------------------------------------------------
1 | use rand::{thread_rng, Rng, distributions::Alphanumeric};
2 |
3 | pub fn gen_random_str() -> String {
4 | thread_rng()
5 | .sample_iter(&Alphanumeric)
6 | .take(300)
7 | .map(char::from)
8 | .collect()
9 | }
10 |
--------------------------------------------------------------------------------
/src-tauri/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "app"
3 | version = "1.4.5"
4 | description = "A way to find mutuals in osu!"
5 | authors = ["metkm"]
6 | license = "MIT"
7 | repository = "https://github.com/metkm/osu-mutual-finder"
8 | default-run = "app"
9 | edition = "2021"
10 | rust-version = "1.57"
11 |
12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
13 |
14 | [build-dependencies]
15 | tauri-build = { version = "1.2", features = [] }
16 |
17 | [dependencies]
18 | # serde_json = "1.0"
19 | # serde = { version = "1.0", features = ["derive"] }
20 | tauri = { version = "1.2.2", features = ["devtools", "http-request", "process-relaunch", "shell-open", "updater", "window-close", "window-maximize", "window-minimize", "window-show", "window-start-dragging", "window-unmaximize"] }
21 | window-shadows = "0.2.0"
22 |
23 | [profile.release]
24 | panic = "abort" # Strip expensive panic clean-up logic
25 | codegen-units = 1 # Compile crates one after another so the compiler can optimize better
26 | lto = true # Enables link to optimizations
27 | opt-level = "s" # Optimize for binary size
28 | strip = true # Automatically strip symbols from the binary.
29 |
30 | [features]
31 | # by default Tauri runs in production mode
32 | # when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
33 | default = [ "custom-protocol" ]
34 | # this feature is used used for production builds where `devPath` points to the filesystem
35 | # DO NOT remove this
36 | custom-protocol = [ "tauri/custom-protocol" ]
37 |
--------------------------------------------------------------------------------
/src-tauri/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | tauri_build::build()
3 | }
4 |
--------------------------------------------------------------------------------
/src-tauri/icons/128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metkm/osu-mutual-finder/091387f624f27750134fa10ad0428846f945de29/src-tauri/icons/128x128.png
--------------------------------------------------------------------------------
/src-tauri/icons/128x128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metkm/osu-mutual-finder/091387f624f27750134fa10ad0428846f945de29/src-tauri/icons/128x128@2x.png
--------------------------------------------------------------------------------
/src-tauri/icons/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metkm/osu-mutual-finder/091387f624f27750134fa10ad0428846f945de29/src-tauri/icons/icon.icns
--------------------------------------------------------------------------------
/src-tauri/icons/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metkm/osu-mutual-finder/091387f624f27750134fa10ad0428846f945de29/src-tauri/icons/icon.ico
--------------------------------------------------------------------------------
/src-tauri/src/main.rs:
--------------------------------------------------------------------------------
1 | #![cfg_attr(
2 | all(not(debug_assertions), target_os = "windows"),
3 | windows_subsystem = "windows"
4 | )]
5 |
6 | use tauri::{Manager, WindowEvent};
7 | use window_shadows::set_shadow;
8 |
9 | fn main() {
10 | tauri::Builder::default()
11 | .setup(|app| {
12 | let Some(window) = app.get_window("main") else {
13 | return Ok(())
14 | };
15 |
16 | if set_shadow(&window, true).is_err() {
17 | println!("error while adding shadow to main window");
18 | }
19 |
20 | Ok(())
21 | })
22 | .on_window_event(|e| {
23 | if let WindowEvent::Resized(_) = e.event() {
24 | std::thread::sleep(std::time::Duration::from_millis(1))
25 | }
26 | })
27 | .invoke_handler(tauri::generate_handler![])
28 | .run(tauri::generate_context!())
29 | .expect("error while running tauri application");
30 | }
31 |
--------------------------------------------------------------------------------
/src/animation.ts:
--------------------------------------------------------------------------------
1 | export const handleBeforeLeave = (element: Element) => {
2 | const el = element as HTMLElement;
3 | const { marginLeft, marginTop, width, height } = window.getComputedStyle(el);
4 |
5 | element.setAttribute("style", `
6 | left: ${el.offsetLeft - parseInt(marginLeft)}px;
7 | top: ${el.offsetTop - parseInt(marginTop)}px;
8 | width: ${width};
9 | height: ${height};
10 | `);
11 | }
12 |
--------------------------------------------------------------------------------
/src/api/cookies.ts:
--------------------------------------------------------------------------------
1 | const cookieRegex = /(?.*?)=(?.*?);/;
2 |
3 | export const getCookies = (headers: Record) => {
4 | let cookies: Record = {};
5 |
6 | for (const cookie of headers["set-cookie"]) {
7 | let match = cookie.match(cookieRegex);
8 | if (!match || !match.groups || match.groups.value === "deleted") continue;
9 |
10 | if (match.groups.key === "osu_session" && match.groups.value.length < 262) {
11 | continue;
12 | }
13 |
14 | cookies[match.groups.key] = match.groups.value;
15 | }
16 |
17 | return cookies;
18 | }
19 |
20 | export const parseCookies = (cookies: Record) => {
21 | let cookieString = "";
22 |
23 | for (const [key, value] of Object.entries(cookies)) {
24 | cookieString += `${key}=${value}; `;
25 | }
26 |
27 | return cookieString.slice(0, -2);
28 | }
29 |
--------------------------------------------------------------------------------
/src/api/user.ts:
--------------------------------------------------------------------------------
1 | import { http } from "@tauri-apps/api";
2 | import { UserObject } from "../types";
3 | import { useAuthStore } from "../store";
4 |
5 | export const getUserWeb = async (userId: number): Promise => {
6 | const response = await http.fetch(`https://osu.ppy.sh/users/${userId}`, {
7 | method: "GET",
8 | responseType: 2
9 | });
10 | const responseDom = new DOMParser().parseFromString(response.data, "text/html");
11 | let element = responseDom.getElementsByClassName("js-react--profile-page")[0]!;
12 | let userJson = JSON.parse(element.getAttribute("data-initial-data")!);
13 |
14 | return userJson.user
15 | }
16 |
17 | export const getUserApi = async (userId: number, access_token: string): Promise => {
18 | const response = await http.fetch(`https://osu.ppy.sh/api/v2/users/${userId}`, {
19 | method: "GET",
20 | responseType: 1,
21 | headers: {
22 | "Authorization": `Bearer ${access_token}`
23 | }
24 | });
25 |
26 | return response.data
27 | }
28 |
29 | export const getUser = async (userId: number) => {
30 | const authStore = useAuthStore();
31 |
32 | if (authStore.access_token) {
33 | return getUserApi(userId, authStore.access_token);
34 | }
35 |
36 | return getUserWeb(userId);
37 | }
38 |
--------------------------------------------------------------------------------
/src/assets/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metkm/osu-mutual-finder/091387f624f27750134fa10ad0428846f945de29/src/assets/icon.ico
--------------------------------------------------------------------------------
/src/components/AppList.vue:
--------------------------------------------------------------------------------
1 |
52 |
53 |
54 |
62 |
63 |
--------------------------------------------------------------------------------
/src/components/AppSide.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
{{ title }}
12 |
{{ desc }}
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/components/AppTitleBar.vue:
--------------------------------------------------------------------------------
1 |
23 |
24 |
25 |
48 |
49 |
--------------------------------------------------------------------------------
/src/components/AppVersion.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
version
10 |
{{ version }}
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/components/DevRouter.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
13 | {{ route.name }}
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/components/Icons/Back.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
--------------------------------------------------------------------------------
/src/components/Icons/Clear.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/src/components/Icons/Close.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/src/components/Icons/Login.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/src/components/Icons/Maximize.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/src/components/Icons/Minimize.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/src/components/Icons/Settings.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/src/components/Icons/Spinner.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/src/components/Icons/UnMaximize.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/src/components/Login/LoginMutuals.vue:
--------------------------------------------------------------------------------
1 |
21 |
22 |
23 |
25 |
26 |
Found Mutuals
27 |
mutuals that are found from the database of mutual finder.
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/components/Settings/SettingsBase.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/components/Settings/SettingsBlacklist.vue:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
27 | User IDs to skip automatically
28 |
29 |
30 |
31 |
32 | Add to Blacklist
33 | Clear Blacklist
34 |
35 |
36 |
37 |
38 | -
40 | {{ id }}
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/components/Settings/SettingsCountries.vue:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/components/Settings/SettingsCountriesGlobal.vue:
--------------------------------------------------------------------------------
1 |
2 | Mutual Finder will start to check users that are on the global rankings
3 |
4 |
--------------------------------------------------------------------------------
/src/components/Settings/SettingsDelay.vue:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 | Cooldown before adding a person to see if an user is mutual or not. In milliseconds
15 |
16 |
17 |
18 |
19 | Cooldown before skipping to page. In milliseconds
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/components/Settings/SettingsMode.vue:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 | Gamemode
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/components/Settings/SettingsOther.vue:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 |
20 |
21 | Remove friend
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/components/Settings/SettingsPageLimit.vue:
--------------------------------------------------------------------------------
1 |
30 |
31 |
32 |
33 | Country Limits
34 |
35 |
36 |
Code
37 |
Start
38 |
End
39 |
Index
40 |
41 |
42 |
43 | -
44 |
45 | {{ limit.countryCode }}
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/src/components/Settings/SettingsToggle.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
11 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/components/Ui/BaseButton.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
35 |
36 |
37 |
54 |
--------------------------------------------------------------------------------
/src/components/Ui/BaseButtonIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
20 |
21 |
--------------------------------------------------------------------------------
/src/components/Ui/BaseCheckbox.vue:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 |
23 |
36 |
37 |
38 |
39 | {{ description }}
40 |
41 |
--------------------------------------------------------------------------------
/src/components/Ui/BaseInput.vue:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
27 |
28 |
--------------------------------------------------------------------------------
/src/components/Ui/BaseInputNumber.vue:
--------------------------------------------------------------------------------
1 |
32 |
33 |
34 |
35 |
36 |
39 |
40 |
41 |
{{ modelValue }}
42 |
43 |
44 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/components/Ui/BaseRadio.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
17 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/components/Ui/BaseSuspense.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/User.vue:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
![]()
26 |
{{ user.username }}
27 |
#{{ user.statistics.global_rank }}
28 |
29 |
![]()
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module "*.vue" {
4 | import { DefineComponent } from "vue";
5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
6 | const component: DefineComponent<{}, {}, any>;
7 | export default component;
8 | }
9 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | /* ./src/index.css */
2 | @tailwind base;
3 | @tailwind components;
4 | @tailwind utilities;
5 |
6 | @layer base {
7 | html, body {
8 | @apply h-full;
9 | @apply dark:bg-dark;
10 | @apply overflow-hidden relative;
11 | font-family: "Inter";
12 | }
13 |
14 | * {
15 | @apply outline-none;
16 | }
17 |
18 | ::-webkit-scrollbar {
19 | @apply w-4;
20 | @apply rounded-full;
21 | }
22 |
23 | ::-webkit-scrollbar-thumb {
24 | background-clip: padding-box;
25 |
26 | @apply bg-neutral-200 dark:bg-neutral-800;
27 | @apply border-5 border-solid border-transparent;
28 | @apply rounded-full;
29 | }
30 |
31 | .page {
32 | @apply h-full;
33 | @apply p-2 overflow-hidden;
34 | }
35 |
36 | .input-number::-webkit-inner-spin-button {
37 | -webkit-appearance: none;
38 | }
39 |
40 | .form-tick {
41 | @apply appearance-none transition-all;
42 | @apply w-5 h-5 rounded-md;
43 | @apply border dark:border-neutral-800 checked:bg-green-500;
44 | @apply focus-outline;
45 | }
46 | }
47 |
48 | @layer utilities {
49 | .title-btn {
50 | width: 46px;
51 | height: 32px;
52 |
53 | @apply flex justify-center items-center;
54 | @apply hover:bg-black hover:bg-opacity-5 dark:hover:bg-white dark:hover:bg-opacity-10;
55 | }
56 |
57 | .title-btn.close {
58 | @apply hover:bg-[#e81123];
59 | }
60 |
61 | .window-btns svg {
62 | width: 10px;
63 | height: 10px;
64 | }
65 |
66 | .focus-outline {
67 | @apply focus:outline-2 focus:outline-green-600 focus:dark:outline-green-900;
68 | @apply outline-offset-0;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from "vue";
2 | import { createPinia } from "pinia";
3 | import piniaPluginPersistedState from "pinia-plugin-persistedstate";
4 | import App from "./App.vue";
5 | import Router from "./router/index";
6 | import Notification from "./plugin/notification";
7 | import "./index.css";
8 |
9 | const pinia = createPinia();
10 | pinia.use(piniaPluginPersistedState);
11 |
12 | createApp(App)
13 | .use(Router)
14 | .use(pinia)
15 | .use(Notification)
16 | .mount("#app");
17 |
--------------------------------------------------------------------------------
/src/plugin/notification.ts:
--------------------------------------------------------------------------------
1 | import { App } from "vue";
2 | import { NotificationOptions } from "../types";
3 | import Notification from "./Notification.vue";
4 | import mitt from "mitt";
5 |
6 | export default {
7 | install(app: App) {
8 | app.component("Notification", Notification);
9 | }
10 | }
11 |
12 | type Events = {
13 | notify: {
14 | text: string,
15 | options?: NotificationOptions
16 | },
17 | notifyRemove: {
18 | text: string
19 | }
20 | }
21 |
22 | export const events = mitt()
23 | export function notify(text: string, options?: NotificationOptions) {
24 | events.emit("notify", {
25 | text,
26 | options
27 | })
28 | }
29 |
30 | export function notifyRemove(text: string) {
31 | events.emit("notifyRemove", {
32 | text
33 | })
34 | }
35 |
--------------------------------------------------------------------------------
/src/router/index.ts:
--------------------------------------------------------------------------------
1 | import { createRouter, createWebHistory } from "vue-router";
2 |
3 | import Login from "../views/Login.vue";
4 | import Verify from "../views/Verify.vue";
5 | import Mutuals from "../views/Mutuals.vue";
6 | import Settings from "../views/Settings.vue";
7 |
8 | export default createRouter({
9 | history: createWebHistory(),
10 | routes: [
11 | {
12 | path: "/",
13 | name: "Login",
14 | component: Login
15 | },
16 | {
17 | path: "/verify",
18 | name: "Verify",
19 | component: Verify
20 | },
21 | {
22 | path: "/mutuals",
23 | name: "Mutuals",
24 | component: Mutuals
25 | },
26 | {
27 | path: "/settings",
28 | name: "Settings",
29 | component: Settings
30 | }
31 | ]
32 | })
33 |
--------------------------------------------------------------------------------
/src/store/auth.ts:
--------------------------------------------------------------------------------
1 | import { defineStore } from "pinia";
2 | import ky from "ky";
3 |
4 | interface APIRefreshResponse {
5 | access_token: string,
6 | refresh_token: string
7 | }
8 |
9 | export const useAuthStore = defineStore("auth", {
10 | state: () => ({
11 | token: "",
12 | session: "",
13 | access_token: "",
14 | refresh_token: ""
15 | }),
16 | actions: {
17 | async refreshTokens() {
18 | const response = await ky.patch(`${import.meta.env.VITE_API_BASE_URL}/api/refresh`, {
19 | credentials: "include",
20 | }).json();
21 |
22 | this.access_token = response.access_token;
23 | this.refresh_token = response.refresh_token;
24 | }
25 | },
26 | persist: true
27 | });
28 |
--------------------------------------------------------------------------------
/src/store/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./settings";
2 | export * from "./user";
3 | export * from "./auth";
4 |
--------------------------------------------------------------------------------
/src/store/settings.ts:
--------------------------------------------------------------------------------
1 | import { defineStore } from "pinia";
2 | import { Check, Gamemode, WebCountry } from "../types";
3 | import { clampNumber } from "../utils";
4 |
5 | interface Limit {
6 | countryCode: string;
7 | start: number; // page start
8 | end: number; // page end
9 | index: number; // start index (only for start page)
10 | }
11 |
12 | const toggleSetting = (array: T[], item: T) => {
13 | if (!array.includes(item)) {
14 | array.push(item);
15 | return;
16 | }
17 |
18 | let index = array.findIndex(x => x == item);
19 | if (index !== -1) {
20 | array.splice(index, 1);
21 | }
22 | }
23 |
24 | export const clampLimit = (limit: Limit) => {
25 | limit.start = clampNumber(limit.start, 1, 200);
26 | limit.end = clampNumber(limit.end, 1, 200);
27 | limit.index = clampNumber(limit.index, 0, 50);
28 | };
29 |
30 | export const useSettingsStore = defineStore("settings", {
31 | state: () => ({
32 | friends: [] as number[],
33 | blacklistIds: [] as number[],
34 | countries: [] as WebCountry[],
35 | limits: [] as Limit[],
36 | addFriend: false,
37 | addBlacklist: false,
38 | gamemode: Gamemode.osu,
39 | check: Check.Country,
40 | friendAddDelay: 6000,
41 | pageSkipDelay: 2500,
42 | uploaded: false
43 | }),
44 | actions: {
45 | toggleBlacklistId(userId: number) {
46 | toggleSetting(this.blacklistIds, userId);
47 | },
48 | updateLimit(newLimit: Limit) {
49 | clampLimit(newLimit);
50 |
51 | let index = this.limits.findIndex(x => x.countryCode == newLimit.countryCode);
52 | if (index !== -1) {
53 | this.limits.splice(index, 1, newLimit);
54 | } else {
55 | this.limits.push(newLimit);
56 | }
57 | }
58 | },
59 | getters: {
60 | getLimit: (state) => {
61 | return (code: string) => state.limits.find(limit => limit.countryCode == code)
62 | }
63 | },
64 | persist: true
65 | });
66 |
--------------------------------------------------------------------------------
/src/store/user.ts:
--------------------------------------------------------------------------------
1 | import { defineStore } from "pinia";
2 | import { SessionLoginUser } from "../types";
3 |
4 | export const useUserStore = defineStore<"user",{ user?: SessionLoginUser }>("user", {
5 | state: () => ({
6 | user: undefined
7 | }),
8 | persist: true
9 | });
10 |
--------------------------------------------------------------------------------
/src/views/Settings.vue:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/views/Verify.vue:
--------------------------------------------------------------------------------
1 |
29 |
30 |
31 |
44 |
45 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
3 | darkMode: "media", // or 'media' or 'class'
4 | theme: {
5 | extend: {
6 | colors: {
7 | "dark": "#101010"
8 | },
9 | borderWidth: {
10 | "5": "5px"
11 | }
12 | },
13 | },
14 | plugins: [],
15 | }
16 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "useDefineForClassFields": true,
5 | "module": "esnext",
6 | "moduleResolution": "node",
7 | "strict": true,
8 | "jsx": "preserve",
9 | "sourceMap": true,
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "esModuleInterop": true,
13 | "lib": ["esnext", "dom"],
14 | "skipLibCheck": true
15 | },
16 | "vueCompilerOptions": {
17 | "jsxTemplates": true,
18 | "experimentalRfc436": true
19 | },
20 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
21 | "references": [{ "path": "./tsconfig.node.json" }]
22 | }
23 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "esnext",
5 | "moduleResolution": "node"
6 | },
7 | "include": ["vite.config.ts"]
8 | }
9 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import vue from '@vitejs/plugin-vue'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [vue()],
7 | // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
8 | // prevent vite from obscuring rust errors
9 | clearScreen: false,
10 | // tauri expects a fixed port, fail if that port is not available
11 | server: {
12 | port: 1420,
13 | strictPort: true,
14 | },
15 | // to make use of `TAURI_DEBUG` and other env variables
16 | // https://tauri.studio/v1/api/config#buildconfig.beforedevcommand
17 | envPrefix: ["VITE_", "TAURI_"],
18 | build: {
19 | // Tauri supports es2021
20 | target: ["es2021", "chrome100", "safari13"],
21 | // don't minify for debug builds
22 | minify: !process.env.TAURI_DEBUG ? "esbuild" : false,
23 | // produce sourcemaps for debug builds
24 | sourcemap: !!process.env.TAURI_DEBUG,
25 | },
26 | });
27 |
--------------------------------------------------------------------------------