├── .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 | ![image](https://github.com/user-attachments/assets/718d6142-d6c0-47fb-8049-390d4edb466f) 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 | ![mutuals](https://user-images.githubusercontent.com/54271295/215283839-d6751112-21a9-4b5a-b7b5-8bae26b6914b.png) 16 | ![settings](https://user-images.githubusercontent.com/54271295/181880677-24d09633-95c0-4ae9-a715-7fc9da76865c.png) 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 | 63 | -------------------------------------------------------------------------------- /src/components/AppSide.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 22 | -------------------------------------------------------------------------------- /src/components/AppTitleBar.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 49 | -------------------------------------------------------------------------------- /src/components/AppVersion.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | -------------------------------------------------------------------------------- /src/components/DevRouter.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | -------------------------------------------------------------------------------- /src/components/Icons/Back.vue: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /src/components/Icons/Clear.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/components/Icons/Close.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/components/Icons/Login.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/components/Icons/Maximize.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/components/Icons/Minimize.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/components/Icons/Settings.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Icons/Spinner.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/components/Icons/UnMaximize.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Login/LoginMutuals.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 35 | -------------------------------------------------------------------------------- /src/components/Settings/SettingsBase.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/components/Settings/SettingsBlacklist.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 46 | -------------------------------------------------------------------------------- /src/components/Settings/SettingsCountries.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 29 | -------------------------------------------------------------------------------- /src/components/Settings/SettingsCountriesGlobal.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/components/Settings/SettingsDelay.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 24 | -------------------------------------------------------------------------------- /src/components/Settings/SettingsMode.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 22 | -------------------------------------------------------------------------------- /src/components/Settings/SettingsOther.vue: -------------------------------------------------------------------------------- 1 | 17 | 25 | -------------------------------------------------------------------------------- /src/components/Settings/SettingsPageLimit.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 62 | -------------------------------------------------------------------------------- /src/components/Settings/SettingsToggle.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | -------------------------------------------------------------------------------- /src/components/Ui/BaseButton.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 36 | 37 | 54 | -------------------------------------------------------------------------------- /src/components/Ui/BaseButtonIcon.vue: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /src/components/Ui/BaseCheckbox.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | -------------------------------------------------------------------------------- /src/components/Ui/BaseInput.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 28 | -------------------------------------------------------------------------------- /src/components/Ui/BaseInputNumber.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 50 | -------------------------------------------------------------------------------- /src/components/Ui/BaseRadio.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 22 | -------------------------------------------------------------------------------- /src/components/Ui/BaseSuspense.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | -------------------------------------------------------------------------------- /src/components/User.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | -------------------------------------------------------------------------------- /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 | 26 | -------------------------------------------------------------------------------- /src/views/Verify.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 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 | --------------------------------------------------------------------------------