mirror of
https://github.com/Gimanh/taskview-community.git
synced 2026-09-11 21:38:56 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9091d576e1 | |||
| 7d9e4a811a | |||
| 1b03e61d2b |
+3
-1
@@ -36,4 +36,6 @@ customer-license
|
||||
!build-dockers.sh
|
||||
!build-docker-api.sh
|
||||
!build-docker-web.sh
|
||||
!entrypoint.sh
|
||||
!entrypoint.sh
|
||||
*.private
|
||||
*.private*
|
||||
@@ -4,6 +4,7 @@ import helmet from 'helmet';
|
||||
import { appUserMiddleware } from './middlewares/app-user-middleware';
|
||||
import errorHandler from './middlewares/error-handler';
|
||||
import routes from './routes';
|
||||
import passport, { initPassportLogin } from './tv-modules/auth/strategies/passport-login';
|
||||
|
||||
const VRS = '1.18.0';
|
||||
|
||||
@@ -18,6 +19,8 @@ export default class App {
|
||||
this.initializeMiddlewares();
|
||||
this.initializeRoutes();
|
||||
this.app.use(errorHandler);
|
||||
this.app.use(passport.initialize());
|
||||
initPassportLogin();
|
||||
}
|
||||
|
||||
private initializeMiddlewares() {
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
import { generateString, isEmail, time } from '../../utils/helpers';
|
||||
import EnEmailTemplate from './mail/confirm-email-en';
|
||||
import RuEmailTemplate from './mail/confirm-email-ru';
|
||||
import type { ExternalAuthUser } from './strategies/external-auth.types';
|
||||
|
||||
export default class AuthController {
|
||||
private readonly jwtAlg: Algorithm = process.env.JWT_ALG as Algorithm;
|
||||
@@ -91,6 +92,9 @@ export default class AuthController {
|
||||
return this.makeidLogin(16);
|
||||
}
|
||||
|
||||
generateLoginCode() {
|
||||
return `${this.makeidLogin(12)}:${Date.now()}`.toLocaleLowerCase();
|
||||
}
|
||||
/**
|
||||
* Register user by email and send login code to the email
|
||||
* @param req
|
||||
@@ -110,7 +114,7 @@ export default class AuthController {
|
||||
|
||||
const { email } = data.data;
|
||||
|
||||
const code = `${this.makeidLogin(12)}:${Date.now()}`.toLocaleLowerCase();
|
||||
const code = this.generateLoginCode();
|
||||
|
||||
$logger.info(data.data, `[AuthController:sendLoginCode] we got data for send login code`);
|
||||
|
||||
@@ -175,6 +179,77 @@ export default class AuthController {
|
||||
});
|
||||
}
|
||||
|
||||
loginByProvider = async (req: Request, res: Response) => {
|
||||
const user = req.user as ExternalAuthUser;
|
||||
|
||||
if (!user) {
|
||||
return res.status(400).send('User not found');
|
||||
}
|
||||
|
||||
let userData = await req.appUser.authManager.repository.getUserByLogin(
|
||||
user.email,
|
||||
isEmail(user.email)
|
||||
);
|
||||
|
||||
if (!userData) {
|
||||
const password = this.makeidLogin(7);
|
||||
const login = this.makeidLogin(7);
|
||||
|
||||
const id = await req.appUser.authManager.repository.registerUserInDb({
|
||||
login,
|
||||
email: user.email,
|
||||
password: hashSync(password, 10),
|
||||
block: 0,
|
||||
confirmEmailCode: '',
|
||||
});
|
||||
|
||||
if (!id) {
|
||||
$logger.error(`Can not register user ${user.email} & login ${login}`);
|
||||
return res.status(500).send(`Can not register user ${user.email} & login ${login}`);
|
||||
}
|
||||
|
||||
userData = await req.appUser.authManager.repository.getUserByLogin(
|
||||
user.email,
|
||||
isEmail(user.email)
|
||||
);
|
||||
}
|
||||
|
||||
if (!userData) {
|
||||
$logger.error(`Can not find user ${user.email} after registration`);
|
||||
return res.status(500).send(`Can not find user ${user.email} after registration`);
|
||||
}
|
||||
|
||||
const code = this.generateLoginCode();
|
||||
|
||||
const result = await req.appUser.authManager.repository.updateLoginCode(code, userData.email);
|
||||
|
||||
if (!result) {
|
||||
$logger.error(`Can not update login code for user ${userData.email}`);
|
||||
return res.status(500).send(`Can not update login code for user`);
|
||||
}
|
||||
|
||||
const authData = {
|
||||
code: code.split(':')[0],
|
||||
email: userData.email,
|
||||
};
|
||||
|
||||
const encodedAuthData = encodeURIComponent(JSON.stringify(authData));
|
||||
|
||||
try {
|
||||
const platformData = JSON.parse(req.query.state as string);
|
||||
|
||||
if (platformData.platform === "mobile") {
|
||||
return res.redirect(
|
||||
`taskview://login?tokens=${encodedAuthData}`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
$logger.info(`Can not parse platform data from state: ${req.query.state}`);
|
||||
}
|
||||
|
||||
return res.redirect(`${process.env.APP_URL}/login?tokens=${encodedAuthData}`);
|
||||
}
|
||||
|
||||
loginByCode = async (req: Request, res: Response) => {
|
||||
const schema = z.object({
|
||||
email: z.string().email().toLowerCase(),
|
||||
@@ -202,7 +277,12 @@ export default class AuthController {
|
||||
}
|
||||
|
||||
if (tokenFromDb[0] !== data.data.code) {
|
||||
return res.status(400).end();
|
||||
return res.status(400).send({ message: 'Invalid code' });
|
||||
}
|
||||
|
||||
if (tokenFromDb[1] && Date.now() - +tokenFromDb[1] > 60 * 1000) {
|
||||
await req.appUser.authManager.repository.updateLoginCode(null, userData.email);
|
||||
return res.status(400).send({ message: 'Code expired, get new code' });
|
||||
}
|
||||
|
||||
const tokenRowId = await req.appUser.authManager.jwtStorage.initTokenRecord(userData.id);
|
||||
@@ -225,11 +305,7 @@ export default class AuthController {
|
||||
$logger.error(`Can not update tokens in JWT Storage for user ${userData.id} and rowId ${tokenRowId}`);
|
||||
}
|
||||
|
||||
try {
|
||||
await req.appUser.authManager.repository.updateLoginCode(null, userData.email);
|
||||
} catch (_err: unknown) {
|
||||
$logger.error(`Can not update updateLoginCode after login`);
|
||||
}
|
||||
await req.appUser.authManager.repository.updateLoginCode(null, userData.email);
|
||||
|
||||
return res.json(tokens);
|
||||
};
|
||||
|
||||
@@ -42,6 +42,7 @@ export default class AuthModel {
|
||||
return !!(data.rowCount && data.rowCount > 0);
|
||||
} catch (error: unknown) {
|
||||
$logger.error(error, 'Can not update login code');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Router } from 'express';
|
||||
import { Router, type NextFunction, type Request, type Response } from 'express';
|
||||
import type { Routable } from '../../types/routable.type';
|
||||
import AuthController from './AuthController';
|
||||
import { IsLoggedIn } from './middlewares/is-logged-in';
|
||||
|
||||
import passport from './strategies/passport-login';
|
||||
import { ExternalProviderScope } from './strategies/external-auth.types';
|
||||
export default class AuthRoutes implements Routable {
|
||||
private readonly router: ReturnType<typeof Router>;
|
||||
private readonly authController: AuthController;
|
||||
@@ -29,5 +30,23 @@ export default class AuthRoutes implements Routable {
|
||||
this.router.post('/refresh/token', this.authController.refreshTokens);
|
||||
this.router.post('/delete/account/code', [IsLoggedIn], this.authController.sendDeleteAccountCode);
|
||||
this.router.post('/delete/account', [IsLoggedIn], this.authController.deleteUserAccaunt);
|
||||
|
||||
this.router.get(
|
||||
'/provider/:providerName',
|
||||
(req: Request, res: Response, next: NextFunction) => passport.authenticate(req.params.providerName, {
|
||||
scope: ExternalProviderScope[req.params.providerName],
|
||||
session: false,
|
||||
state: JSON.stringify({
|
||||
platform: req.query.platform || '',
|
||||
})
|
||||
})(req, res, next)
|
||||
);
|
||||
this.router.get(
|
||||
'/provider/:providerName/callback',
|
||||
(req: Request, res: Response, next: NextFunction) => passport.authenticate(req.params.providerName, {
|
||||
scope: ExternalProviderScope[req.params.providerName], session: false
|
||||
})(req, res, next),
|
||||
this.authController.loginByProvider
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
export type ExternalAuthUser = {
|
||||
email: string;
|
||||
provider: 'google' | 'github' | string;
|
||||
}
|
||||
|
||||
export const ExternalProviderScope: Record<ExternalAuthUser['provider'], string[]> = {
|
||||
google: ["email"],
|
||||
github: ["user:email"],
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import passport from "passport";
|
||||
import { Strategy as GitHubStrategy } from "passport-github2";
|
||||
import { $logger } from "../../../modules/logget";
|
||||
import type { ExternalAuthUser } from "./external-auth.types";
|
||||
import type { Profile } from "passport-github2";
|
||||
import type { VerifyCallback } from "passport-google-oauth20";
|
||||
|
||||
export function initGithubStrategy() {
|
||||
if (!process.env.GITHUB_CLIENT_ID || !process.env.GITHUB_CLIENT_SECRET || !process.env.GITHUB_CALLBACK_URL) {
|
||||
$logger.warn("GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, and GITHUB_CALLBACK_URL must be set");
|
||||
console.warn("GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, and GITHUB_CALLBACK_URL must be set");
|
||||
return;
|
||||
}
|
||||
|
||||
const options = {
|
||||
clientID: process.env.GITHUB_CLIENT_ID,
|
||||
clientSecret: process.env.GITHUB_CLIENT_SECRET,
|
||||
callbackURL: process.env.GITHUB_CALLBACK_URL,
|
||||
scope: ["user:email"],
|
||||
}
|
||||
|
||||
passport.use(new GitHubStrategy(options, async (_accessToken: string, _refreshToken: string, profile: Profile, done: VerifyCallback) => {
|
||||
try {
|
||||
const email = profile.emails?.[0]?.value;
|
||||
if (!email) return done(null, false);
|
||||
|
||||
const user: ExternalAuthUser = {
|
||||
email,
|
||||
provider: "github",
|
||||
};
|
||||
|
||||
done(null, user);
|
||||
} catch (e) {
|
||||
done(e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import passport from "passport";
|
||||
import { Strategy as GoogleStrategy } from "passport-google-oauth20";
|
||||
import { $logger } from "../../../modules/logget";
|
||||
import type { ExternalAuthUser } from "./external-auth.types";
|
||||
|
||||
export function initGoogleStrategy() {
|
||||
if (!process.env.GOOGLE_CLIENT_ID || !process.env.GOOGLE_CLIENT_SECRET || !process.env.GOOGLE_CALLBACK_URL) {
|
||||
$logger.warn("GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and GOOGLE_CALLBACK_URL must be set");
|
||||
console.warn("GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and GOOGLE_CALLBACK_URL must be set");
|
||||
return;
|
||||
}
|
||||
const options = {
|
||||
clientID: process.env.GOOGLE_CLIENT_ID,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
||||
callbackURL: process.env.GOOGLE_CALLBACK_URL,
|
||||
}
|
||||
|
||||
passport.use(new GoogleStrategy(options, async (_accessToken, _refreshToken, profile, done) => {
|
||||
try {
|
||||
const email = profile.emails?.[0]?.value;
|
||||
if (!email || !profile._json.email_verified) return done(null, false);
|
||||
|
||||
const user: ExternalAuthUser = {
|
||||
email,
|
||||
provider: "google",
|
||||
};
|
||||
|
||||
done(null, user);
|
||||
} catch (e) {
|
||||
done(e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import passport from "passport";
|
||||
import { initGoogleStrategy } from "./google.strategy";
|
||||
import { initGithubStrategy } from "./github.strategy";
|
||||
|
||||
export function initPassportLogin() {
|
||||
initGoogleStrategy();
|
||||
initGithubStrategy();
|
||||
}
|
||||
|
||||
export default passport;
|
||||
|
||||
+8
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "taskview-ce-monorepo",
|
||||
"version": "1.18.2",
|
||||
"version": "1.19.0",
|
||||
"private": true,
|
||||
"description": "TaskView CE monorepo containing web, API, and packages",
|
||||
"workspaces": [
|
||||
@@ -30,7 +30,13 @@
|
||||
"pnpm": ">=8.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/passport": "^1.0.17",
|
||||
"@types/passport-github2": "^1.2.9",
|
||||
"@types/passport-google-oauth20": "^2.0.17",
|
||||
"arktype": "2.1.20",
|
||||
"drizzle-arktype": "0.1.3"
|
||||
"drizzle-arktype": "0.1.3",
|
||||
"passport": "^0.7.0",
|
||||
"passport-github2": "^0.1.12",
|
||||
"passport-google-oauth20": "^2.0.0"
|
||||
}
|
||||
}
|
||||
Generated
+137
-1
@@ -8,12 +8,30 @@ importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
'@types/passport':
|
||||
specifier: ^1.0.17
|
||||
version: 1.0.17
|
||||
'@types/passport-github2':
|
||||
specifier: ^1.2.9
|
||||
version: 1.2.9
|
||||
'@types/passport-google-oauth20':
|
||||
specifier: ^2.0.17
|
||||
version: 2.0.17
|
||||
arktype:
|
||||
specifier: 2.1.20
|
||||
version: 2.1.20
|
||||
drizzle-arktype:
|
||||
specifier: 0.1.3
|
||||
version: 0.1.3(arktype@2.1.20)(drizzle-orm@0.44.4(@types/pg@8.15.5)(bun-types@1.3.3)(pg@8.16.3))
|
||||
passport:
|
||||
specifier: ^0.7.0
|
||||
version: 0.7.0
|
||||
passport-github2:
|
||||
specifier: ^0.1.12
|
||||
version: 0.1.12
|
||||
passport-google-oauth20:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^18.19.119
|
||||
@@ -204,6 +222,9 @@ importers:
|
||||
'@capacitor/app':
|
||||
specifier: 7.0.1
|
||||
version: 7.0.1(@capacitor/core@7.4.4)
|
||||
'@capacitor/browser':
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3(@capacitor/core@7.4.4)
|
||||
'@capacitor/core':
|
||||
specifier: ^7.0.0
|
||||
version: 7.4.4
|
||||
@@ -1043,6 +1064,11 @@ packages:
|
||||
engines: {node: '>=10.3.0'}
|
||||
hasBin: true
|
||||
|
||||
'@capacitor/browser@7.0.3':
|
||||
resolution: {integrity: sha512-PTHG+rj6Sz/6xXOmCs5sQXYDDueD6uv7p/bXOpdNlvV6Dv6yAOlZ85cu0sEggcGzXRW4PZXx/PvCOZasznsTTg==}
|
||||
peerDependencies:
|
||||
'@capacitor/core': '>=7.0.0'
|
||||
|
||||
'@capacitor/cli@5.7.8':
|
||||
resolution: {integrity: sha512-qN8LDlREMhrYhOvVXahoJVNkP8LP55/YPRJrzTAFrMqlNJC18L3CzgWYIblFPnuwfbH/RxbfoZT/ydkwgVpMrw==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
@@ -2372,6 +2398,21 @@ packages:
|
||||
'@types/normalize-package-data@2.4.4':
|
||||
resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
|
||||
|
||||
'@types/oauth@0.9.6':
|
||||
resolution: {integrity: sha512-H9TRCVKBNOhZZmyHLqFt9drPM9l+ShWiqqJijU1B8P3DX3ub84NjxDuy+Hjrz+fEca5Kwip3qPMKNyiLgNJtIA==}
|
||||
|
||||
'@types/passport-github2@1.2.9':
|
||||
resolution: {integrity: sha512-/nMfiPK2E6GKttwBzwj0Wjaot8eHrM57hnWxu52o6becr5/kXlH/4yE2v2rh234WGvSgEEzIII02Nc5oC5xEHA==}
|
||||
|
||||
'@types/passport-google-oauth20@2.0.17':
|
||||
resolution: {integrity: sha512-MHNOd2l7gOTCn3iS+wInPQMiukliAUvMpODO3VlXxOiwNEMSyzV7UNvAdqxSN872o8OXx1SqPDVT6tLW74AtqQ==}
|
||||
|
||||
'@types/passport-oauth2@1.8.0':
|
||||
resolution: {integrity: sha512-6//z+4orIOy/g3zx17HyQ71GSRK4bs7Sb+zFasRoc2xzlv7ZCJ+vkDBYFci8U6HY+or6Zy7ajf4mz4rK7nsWJQ==}
|
||||
|
||||
'@types/passport@1.0.17':
|
||||
resolution: {integrity: sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==}
|
||||
|
||||
'@types/pg@8.15.5':
|
||||
resolution: {integrity: sha512-LF7lF6zWEKxuT3/OR8wAZGzkg4ENGXFNyiV/JeOt9z5B+0ZVwbql9McqX5c/WStFq1GaGso7H1AzP/qSzmlCKQ==}
|
||||
|
||||
@@ -3065,6 +3106,10 @@ packages:
|
||||
base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
|
||||
base64url@3.0.1:
|
||||
resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
baseline-browser-mapping@2.8.32:
|
||||
resolution: {integrity: sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==}
|
||||
hasBin: true
|
||||
@@ -5311,6 +5356,9 @@ packages:
|
||||
nwsapi@2.2.21:
|
||||
resolution: {integrity: sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==}
|
||||
|
||||
oauth@0.10.2:
|
||||
resolution: {integrity: sha512-JtFnB+8nxDEXgNyniwz573xxbKSOu3R8D40xQKqcjwJ2CDkYqUDI53o6IuzDJBx60Z8VKCm271+t8iFjakrl8Q==}
|
||||
|
||||
object-assign@4.1.1:
|
||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -5419,6 +5467,26 @@ packages:
|
||||
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
passport-github2@0.1.12:
|
||||
resolution: {integrity: sha512-3nPUCc7ttF/3HSP/k9sAXjz3SkGv5Nki84I05kSQPo01Jqq1NzJACgMblCK0fGcv9pKCG/KXU3AJRDGLqHLoIw==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
passport-google-oauth20@2.0.0:
|
||||
resolution: {integrity: sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
|
||||
passport-oauth2@1.8.0:
|
||||
resolution: {integrity: sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
|
||||
passport-strategy@1.0.0:
|
||||
resolution: {integrity: sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
|
||||
passport@0.7.0:
|
||||
resolution: {integrity: sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
|
||||
path-browserify@1.0.1:
|
||||
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
|
||||
|
||||
@@ -5480,6 +5548,9 @@ packages:
|
||||
pause-stream@0.0.11:
|
||||
resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==}
|
||||
|
||||
pause@0.0.1:
|
||||
resolution: {integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==}
|
||||
|
||||
pend@1.2.0:
|
||||
resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
|
||||
|
||||
@@ -6727,6 +6798,9 @@ packages:
|
||||
engines: {node: '>=0.8.0'}
|
||||
hasBin: true
|
||||
|
||||
uid2@0.0.4:
|
||||
resolution: {integrity: sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==}
|
||||
|
||||
unbox-primitive@1.1.0:
|
||||
resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -8192,6 +8266,10 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@capacitor/browser@7.0.3(@capacitor/core@7.4.4)':
|
||||
dependencies:
|
||||
'@capacitor/core': 7.4.4
|
||||
|
||||
'@capacitor/cli@5.7.8':
|
||||
dependencies:
|
||||
'@ionic/cli-framework-output': 2.2.8
|
||||
@@ -9430,9 +9508,35 @@ snapshots:
|
||||
|
||||
'@types/normalize-package-data@2.4.4': {}
|
||||
|
||||
'@types/pg@8.15.5':
|
||||
'@types/oauth@0.9.6':
|
||||
dependencies:
|
||||
'@types/node': 22.17.0
|
||||
|
||||
'@types/passport-github2@1.2.9':
|
||||
dependencies:
|
||||
'@types/express': 4.17.23
|
||||
'@types/passport': 1.0.17
|
||||
'@types/passport-oauth2': 1.8.0
|
||||
|
||||
'@types/passport-google-oauth20@2.0.17':
|
||||
dependencies:
|
||||
'@types/express': 4.17.23
|
||||
'@types/passport': 1.0.17
|
||||
'@types/passport-oauth2': 1.8.0
|
||||
|
||||
'@types/passport-oauth2@1.8.0':
|
||||
dependencies:
|
||||
'@types/express': 4.17.23
|
||||
'@types/oauth': 0.9.6
|
||||
'@types/passport': 1.0.17
|
||||
|
||||
'@types/passport@1.0.17':
|
||||
dependencies:
|
||||
'@types/express': 4.17.23
|
||||
|
||||
'@types/pg@8.15.5':
|
||||
dependencies:
|
||||
'@types/node': 18.19.121
|
||||
pg-protocol: 1.10.3
|
||||
pg-types: 2.2.0
|
||||
|
||||
@@ -10301,6 +10405,8 @@ snapshots:
|
||||
|
||||
base64-js@1.5.1: {}
|
||||
|
||||
base64url@3.0.1: {}
|
||||
|
||||
baseline-browser-mapping@2.8.32: {}
|
||||
|
||||
bcrypt-pbkdf@1.0.2:
|
||||
@@ -12636,6 +12742,8 @@ snapshots:
|
||||
|
||||
nwsapi@2.2.21: {}
|
||||
|
||||
oauth@0.10.2: {}
|
||||
|
||||
object-assign@4.1.1: {}
|
||||
|
||||
object-hash@3.0.0: {}
|
||||
@@ -12746,6 +12854,30 @@ snapshots:
|
||||
|
||||
parseurl@1.3.3: {}
|
||||
|
||||
passport-github2@0.1.12:
|
||||
dependencies:
|
||||
passport-oauth2: 1.8.0
|
||||
|
||||
passport-google-oauth20@2.0.0:
|
||||
dependencies:
|
||||
passport-oauth2: 1.8.0
|
||||
|
||||
passport-oauth2@1.8.0:
|
||||
dependencies:
|
||||
base64url: 3.0.1
|
||||
oauth: 0.10.2
|
||||
passport-strategy: 1.0.0
|
||||
uid2: 0.0.4
|
||||
utils-merge: 1.0.1
|
||||
|
||||
passport-strategy@1.0.0: {}
|
||||
|
||||
passport@0.7.0:
|
||||
dependencies:
|
||||
passport-strategy: 1.0.0
|
||||
pause: 0.0.1
|
||||
utils-merge: 1.0.1
|
||||
|
||||
path-browserify@1.0.1: {}
|
||||
|
||||
path-exists@3.0.0: {}
|
||||
@@ -12790,6 +12922,8 @@ snapshots:
|
||||
dependencies:
|
||||
through: 2.3.8
|
||||
|
||||
pause@0.0.1: {}
|
||||
|
||||
pend@1.2.0: {}
|
||||
|
||||
pg-cloudflare@1.2.7:
|
||||
@@ -14181,6 +14315,8 @@ snapshots:
|
||||
uglify-js@3.19.3:
|
||||
optional: true
|
||||
|
||||
uid2@0.0.4: {}
|
||||
|
||||
unbox-primitive@1.1.0:
|
||||
dependencies:
|
||||
call-bound: 1.0.4
|
||||
|
||||
@@ -7,8 +7,8 @@ android {
|
||||
applicationId "com.handscreamgnl.taskview.app"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1150
|
||||
versionName "1.15.0"
|
||||
versionCode 1190
|
||||
versionName "1.19.0"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
aaptOptions {
|
||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||
|
||||
@@ -10,6 +10,7 @@ android {
|
||||
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
|
||||
dependencies {
|
||||
implementation project(':capacitor-app')
|
||||
implementation project(':capacitor-browser')
|
||||
implementation project(':capacitor-device')
|
||||
implementation project(':capacitor-preferences')
|
||||
implementation project(':capacitor-splash-screen')
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
"type": "SINGLE",
|
||||
"filters": [],
|
||||
"attributes": [],
|
||||
"versionCode": 114,
|
||||
"versionName": "1.14",
|
||||
"outputFile": "TaskView-1.14-debug.apk"
|
||||
"versionCode": 1150,
|
||||
"versionName": "1.15.0",
|
||||
"outputFile": "TaskView-1.15.0-debug.apk"
|
||||
}
|
||||
],
|
||||
"elementType": "File",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
@@ -18,6 +18,12 @@
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="taskview" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<provider
|
||||
@@ -25,11 +31,12 @@
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" />
|
||||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
</application>
|
||||
|
||||
<!-- Permissions -->
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
</manifest>
|
||||
</manifest>
|
||||
@@ -5,6 +5,9 @@ project(':capacitor-android').projectDir = new File('../../node_modules/.pnpm/@c
|
||||
include ':capacitor-app'
|
||||
project(':capacitor-app').projectDir = new File('../../node_modules/.pnpm/@capacitor+app@7.0.1_@capacitor+core@7.4.4/node_modules/@capacitor/app/android')
|
||||
|
||||
include ':capacitor-browser'
|
||||
project(':capacitor-browser').projectDir = new File('../../node_modules/.pnpm/@capacitor+browser@7.0.3_@capacitor+core@7.4.4/node_modules/@capacitor/browser/android')
|
||||
|
||||
include ':capacitor-device'
|
||||
project(':capacitor-device').projectDir = new File('../../node_modules/.pnpm/@capacitor+device@7.0.1_@capacitor+core@7.4.4/node_modules/@capacitor/device/android')
|
||||
|
||||
|
||||
@@ -354,14 +354,14 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1.15.0;
|
||||
CURRENT_PROJECT_VERSION = 1.19.0;
|
||||
DEVELOPMENT_TEAM = H2W2SG48JT;
|
||||
INFOPLIST_FILE = App/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = TaskView;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 1.15.0;
|
||||
MARKETING_VERSION = 1.19.0;
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.handscream.taskview.app;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -377,14 +377,14 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1.15.0;
|
||||
CURRENT_PROJECT_VERSION = 1.19.0;
|
||||
DEVELOPMENT_TEAM = H2W2SG48JT;
|
||||
INFOPLIST_FILE = App/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = TaskView;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 1.15.0;
|
||||
MARKETING_VERSION = 1.19.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.handscream.taskview.app;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
</plist>
|
||||
@@ -12,6 +12,7 @@ def capacitor_pods
|
||||
pod 'Capacitor', :path => '../../../node_modules/.pnpm/@capacitor+ios@7.4.4_@capacitor+core@7.4.4/node_modules/@capacitor/ios'
|
||||
pod 'CapacitorCordova', :path => '../../../node_modules/.pnpm/@capacitor+ios@7.4.4_@capacitor+core@7.4.4/node_modules/@capacitor/ios'
|
||||
pod 'CapacitorApp', :path => '../../../node_modules/.pnpm/@capacitor+app@7.0.1_@capacitor+core@7.4.4/node_modules/@capacitor/app'
|
||||
pod 'CapacitorBrowser', :path => '../../../node_modules/.pnpm/@capacitor+browser@7.0.3_@capacitor+core@7.4.4/node_modules/@capacitor/browser'
|
||||
pod 'CapacitorDevice', :path => '../../../node_modules/.pnpm/@capacitor+device@7.0.1_@capacitor+core@7.4.4/node_modules/@capacitor/device'
|
||||
pod 'CapacitorPreferences', :path => '../../../node_modules/.pnpm/@capacitor+preferences@7.0.1_@capacitor+core@7.4.4/node_modules/@capacitor/preferences'
|
||||
pod 'CapacitorSplashScreen', :path => '../../../node_modules/.pnpm/@capacitor+splash-screen@7.0.3_@capacitor+core@7.4.4/node_modules/@capacitor/splash-screen'
|
||||
|
||||
@@ -5,6 +5,8 @@ PODS:
|
||||
- CapacitorCordova
|
||||
- CapacitorApp (7.0.1):
|
||||
- Capacitor
|
||||
- CapacitorBrowser (7.0.3):
|
||||
- Capacitor
|
||||
- CapacitorCordova (7.4.4)
|
||||
- CapacitorDevice (7.0.1):
|
||||
- Capacitor
|
||||
@@ -24,6 +26,7 @@ PODS:
|
||||
DEPENDENCIES:
|
||||
- "Capacitor (from `../../../node_modules/.pnpm/@capacitor+ios@7.4.4_@capacitor+core@7.4.4/node_modules/@capacitor/ios`)"
|
||||
- "CapacitorApp (from `../../../node_modules/.pnpm/@capacitor+app@7.0.1_@capacitor+core@7.4.4/node_modules/@capacitor/app`)"
|
||||
- "CapacitorBrowser (from `../../../node_modules/.pnpm/@capacitor+browser@7.0.3_@capacitor+core@7.4.4/node_modules/@capacitor/browser`)"
|
||||
- "CapacitorCordova (from `../../../node_modules/.pnpm/@capacitor+ios@7.4.4_@capacitor+core@7.4.4/node_modules/@capacitor/ios`)"
|
||||
- "CapacitorDevice (from `../../../node_modules/.pnpm/@capacitor+device@7.0.1_@capacitor+core@7.4.4/node_modules/@capacitor/device`)"
|
||||
- "CapacitorPreferences (from `../../../node_modules/.pnpm/@capacitor+preferences@7.0.1_@capacitor+core@7.4.4/node_modules/@capacitor/preferences`)"
|
||||
@@ -42,6 +45,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../../../node_modules/.pnpm/@capacitor+ios@7.4.4_@capacitor+core@7.4.4/node_modules/@capacitor/ios"
|
||||
CapacitorApp:
|
||||
:path: "../../../node_modules/.pnpm/@capacitor+app@7.0.1_@capacitor+core@7.4.4/node_modules/@capacitor/app"
|
||||
CapacitorBrowser:
|
||||
:path: "../../../node_modules/.pnpm/@capacitor+browser@7.0.3_@capacitor+core@7.4.4/node_modules/@capacitor/browser"
|
||||
CapacitorCordova:
|
||||
:path: "../../../node_modules/.pnpm/@capacitor+ios@7.4.4_@capacitor+core@7.4.4/node_modules/@capacitor/ios"
|
||||
CapacitorDevice:
|
||||
@@ -58,6 +63,7 @@ SPEC CHECKSUMS:
|
||||
BigInt: f668a80089607f521586bbe29513d708491ef2f7
|
||||
Capacitor: 09d9ff8e9618e8c4b3cab2bbee34a17215dd2fef
|
||||
CapacitorApp: d63334c052278caf5d81585d80b21905c6f93f39
|
||||
CapacitorBrowser: 66aa8ff09cdca2a327ce464b113b470e6f667753
|
||||
CapacitorCordova: bf648a636f3c153f652d312ae145fb508b6ffced
|
||||
CapacitorDevice: fe3f190e1d718f4607bdc6b73993433d1c84f409
|
||||
CapacitorPreferences: cbf154e5e5519b7f5ab33817a334dda1e98387f9
|
||||
@@ -66,6 +72,6 @@ SPEC CHECKSUMS:
|
||||
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
|
||||
Version: de5907f2c5d0f3cf21708db7801d1d5401139486
|
||||
|
||||
PODFILE CHECKSUM: 65fabdcdf017e31d75062b30247956106ea17004
|
||||
PODFILE CHECKSUM: 9772ce7df102002849c361cc64e98fb0b609688d
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"dependencies": {
|
||||
"@capacitor/android": "^7.0.0",
|
||||
"@capacitor/app": "7.0.1",
|
||||
"@capacitor/browser": "^7.0.3",
|
||||
"@capacitor/core": "^7.0.0",
|
||||
"@capacitor/device": "7.0.1",
|
||||
"@capacitor/ios": "^7.0.0",
|
||||
|
||||
@@ -22,6 +22,11 @@
|
||||
key="3"
|
||||
@cancel-recovery="setRecoveryMode(false)"
|
||||
/>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<AuthByGoogle />
|
||||
<AuthByGithub />
|
||||
</div>
|
||||
</v-card-text>
|
||||
<v-divider />
|
||||
|
||||
@@ -47,7 +52,7 @@
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script async setup lang="ts">
|
||||
import { computed, reactive } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
@@ -56,6 +61,8 @@ import PasswordRecovery from '@/components/Authentication/PasswordRecovery';
|
||||
import RegistrationForm from '@/components/Authentication/RegistrationForm';
|
||||
import ResetPassword from '@/components/Authentication/ResetPassword';
|
||||
import LoginByCode from '../LoginByCode/LoginByCode.vue';
|
||||
import AuthByGoogle from '@/components/Authentication/AuthByGoogle.vue';
|
||||
import AuthByGithub from '@/components/Authentication/AuthByGithub.vue';
|
||||
|
||||
const data = reactive({
|
||||
recoveryModeActive: false,
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<ExternalAuthBaseBtn
|
||||
class="gsi-material-button"
|
||||
provider="github"
|
||||
>
|
||||
<template #icon>
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 98 96"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"
|
||||
fill="#24292f"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
<span>Sign in with GitHub</span>
|
||||
</ExternalAuthBaseBtn>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import ExternalAuthBaseBtn from '@/components/Authentication/ExternalAuthBaseBtn.vue';
|
||||
</script>
|
||||
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<ExternalAuthBaseBtn
|
||||
class="gsi-material-button"
|
||||
provider="google"
|
||||
>
|
||||
<template #icon>
|
||||
<svg
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 48 48"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
style="display: block;"
|
||||
>
|
||||
<path
|
||||
fill="#EA4335"
|
||||
d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"
|
||||
/>
|
||||
<path
|
||||
fill="#4285F4"
|
||||
d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"
|
||||
/>
|
||||
<path
|
||||
fill="#FBBC05"
|
||||
d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z"
|
||||
/>
|
||||
<path
|
||||
fill="#34A853"
|
||||
d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
d="M0 0h48v48H0z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
<span>Sign in with Google</span>
|
||||
</ExternalAuthBaseBtn>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import ExternalAuthBaseBtn from '@/components/Authentication/ExternalAuthBaseBtn.vue';
|
||||
</script>
|
||||
@@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<button
|
||||
class="gsi-material-button"
|
||||
@click="login"
|
||||
>
|
||||
<div class="gsi-material-button-state" />
|
||||
<div class="gsi-material-button-content-wrapper">
|
||||
<div class="gsi-material-button-icon">
|
||||
<slot name="icon" />
|
||||
</div>
|
||||
<span class="gsi-material-button-contents">
|
||||
<slot />
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useAdditionalServer } from '@/composition/useAdditionalServer';
|
||||
import { Browser } from '@capacitor/browser';
|
||||
import { Capacitor } from '@capacitor/core';
|
||||
|
||||
const props = defineProps<{
|
||||
provider: 'google' | 'github';
|
||||
}>();
|
||||
|
||||
const { mainServer } = await useAdditionalServer();
|
||||
|
||||
const login = async () => {
|
||||
const url = `${mainServer.value}/module/auth/provider/${props.provider}?platform=${
|
||||
Capacitor.isNativePlatform() ? "mobile" : "web"
|
||||
}`;
|
||||
|
||||
if (Capacitor.isNativePlatform()) {
|
||||
await Browser.open({ url });
|
||||
} else {
|
||||
window.location.href = url;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.gsi-material-button {
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-webkit-appearance: none;
|
||||
background-color: WHITE;
|
||||
background-image: none;
|
||||
border: 1px solid #747775;
|
||||
-webkit-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
color: #1f1f1f;
|
||||
cursor: pointer;
|
||||
font-family: 'Roboto', arial, sans-serif;
|
||||
font-size: 14px;
|
||||
height: 40px;
|
||||
letter-spacing: 0.25px;
|
||||
outline: none;
|
||||
overflow: hidden;
|
||||
padding: 0 12px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
-webkit-transition: background-color .218s, border-color .218s, box-shadow .218s;
|
||||
transition: background-color .218s, border-color .218s, box-shadow .218s;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
width: auto;
|
||||
max-width: 400px;
|
||||
min-width: min-content;
|
||||
}
|
||||
|
||||
.gsi-material-button .gsi-material-button-icon {
|
||||
height: 20px;
|
||||
margin-right: 10px;
|
||||
min-width: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.gsi-material-button .gsi-material-button-content-wrapper {
|
||||
-webkit-align-items: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
-webkit-flex-direction: row;
|
||||
flex-direction: row;
|
||||
-webkit-flex-wrap: nowrap;
|
||||
flex-wrap: nowrap;
|
||||
height: 100%;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.gsi-material-button .gsi-material-button-contents {
|
||||
-webkit-flex-grow: 1;
|
||||
flex-grow: 1;
|
||||
font-family: 'Roboto', arial, sans-serif;
|
||||
font-weight: 500;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.gsi-material-button .gsi-material-button-state {
|
||||
-webkit-transition: opacity .218s;
|
||||
transition: opacity .218s;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.gsi-material-button:disabled {
|
||||
cursor: default;
|
||||
background-color: #ffffff61;
|
||||
border-color: #1f1f1f1f;
|
||||
}
|
||||
|
||||
.gsi-material-button:disabled .gsi-material-button-contents {
|
||||
opacity: 38%;
|
||||
}
|
||||
|
||||
.gsi-material-button:disabled .gsi-material-button-icon {
|
||||
opacity: 38%;
|
||||
}
|
||||
|
||||
.gsi-material-button:not(:disabled):active .gsi-material-button-state,
|
||||
.gsi-material-button:not(:disabled):focus .gsi-material-button-state {
|
||||
background-color: #303030;
|
||||
opacity: 12%;
|
||||
}
|
||||
|
||||
.gsi-material-button:not(:disabled):hover {
|
||||
-webkit-box-shadow: 0 1px 2px 0 rgba(60, 64, 67, .30), 0 1px 3px 1px rgba(60, 64, 67, .15);
|
||||
box-shadow: 0 1px 2px 0 rgba(60, 64, 67, .30), 0 1px 3px 1px rgba(60, 64, 67, .15);
|
||||
}
|
||||
|
||||
.gsi-material-button:not(:disabled):hover .gsi-material-button-state {
|
||||
background-color: #303030;
|
||||
opacity: 8%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,14 +0,0 @@
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
import { AppCredentialsForm } from '@/components/Authentication/AppCredentialsForm';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
AppCredentialsForm,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
msg: 'login',
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -1,7 +1,77 @@
|
||||
<template>
|
||||
<div class="justify-center d-flex align-center start-page-height">
|
||||
<app-credentials-form />
|
||||
<Suspense>
|
||||
<AppCredentialsForm />
|
||||
</Suspense>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./LoginPage.ts" lang="ts" />
|
||||
<script setup lang="ts">
|
||||
import { AppCredentialsForm } from '@/components/Authentication/AppCredentialsForm';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { $ls } from '@/plugins/axios';
|
||||
import { onMounted } from 'vue';
|
||||
import type { LoginResponse } from '@/components/Authentication/LoginForm/Types';
|
||||
import { redirectToUser } from '@/helpers/app-helper';
|
||||
import { App } from "@capacitor/app";
|
||||
import { Browser } from '@capacitor/browser';
|
||||
import $api from '@/helpers/axios';
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
type LoginTokens = {
|
||||
code: string;
|
||||
email: string;
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
try{
|
||||
const tokens = route.query.tokens as string;
|
||||
|
||||
if(!tokens){
|
||||
return;
|
||||
}
|
||||
|
||||
const result = JSON.parse(decodeURIComponent(tokens)) as LoginTokens;
|
||||
await loginByCode(result.code, result.email);
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
App.addListener("appUrlOpen", async ({ url }) => {
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (url.startsWith("taskview://login?tokens")) {
|
||||
const parsed = new URL(url);
|
||||
const tokens = parsed.searchParams.get("tokens");
|
||||
|
||||
if(!tokens){
|
||||
return;
|
||||
}
|
||||
|
||||
try{
|
||||
const result = JSON.parse(decodeURIComponent(tokens)) as LoginTokens;
|
||||
await loginByCode(result.code, result.email);
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
await Browser.close();
|
||||
});
|
||||
|
||||
const loginByCode = async (code: string, email: string) => {
|
||||
const result = await $api.post<LoginResponse>('/module/auth/login-by-code', { code, email });
|
||||
if (result && result.data.access) {
|
||||
$ls.setToken(result.data.access);
|
||||
$ls.setRefreshToken(result.data.refresh);
|
||||
await $ls.updateUserStoreByToken();
|
||||
await redirectToUser(router);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import LoginPage from '@/pages/LoginPage/LoginPage.vue';
|
||||
|
||||
export default LoginPage;
|
||||
@@ -67,7 +67,7 @@ const router = createRouter({
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: () => import('@/pages/LoginPage'),
|
||||
component: () => import('@/pages/LoginPage/LoginPage.vue'),
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user