Compare commits

...

16 Commits

Author SHA1 Message Date
Gimanh b3cc983965 Merge pull request #13 from Gimanh/chore/version
chore: version
2026-01-23 21:58:10 +01:00
Nikolai Giman a14c332586 chore: version 2026-01-23 21:57:29 +01:00
Gimanh aa91934f19 Merge pull request #12 from Gimanh/fix/use-cookie
fix: use cookie
2026-01-22 20:03:13 +01:00
Gimanh aa8e6e2da6 Merge branch 'main' into fix/use-cookie 2026-01-22 20:02:40 +01:00
Nikolai Giman c4c9a16a09 fix: use cookie 2026-01-22 20:01:26 +01:00
Gimanh 293340c253 Merge pull request #11 from Gimanh/chore/docker-build
add bash script
2026-01-19 00:19:03 +01:00
Nikolai Giman 04bb545c25 add bash script 2026-01-19 00:17:56 +01:00
Gimanh aa235c10ea Merge pull request #10 from Gimanh/chore/extend-app
chore: add extend methods
2026-01-18 23:57:14 +01:00
Nikolai Giman 45e4a11605 chore: add extend methods 2026-01-18 23:56:23 +01:00
Gimanh 55b42dfecb Merge pull request #9 from Gimanh/chore/deps
chore: deps
2026-01-18 22:28:34 +01:00
Nikolai Giman a620c39b50 chore: deps 2026-01-18 22:24:59 +01:00
Nikolai Giman 8edb2d50d3 chore: deps 2026-01-18 22:17:40 +01:00
Nikolai Giman 19093598a0 chore: deps 2026-01-18 22:13:59 +01:00
Gimanh 9091d576e1 Merge pull request #8 from Gimanh/feat/google-auth
feat: add login by Google and GitHub
2026-01-12 21:36:24 +01:00
Nikolai Giman 7d9e4a811a feat: update app version 2026-01-12 00:40:18 +01:00
Nikolai Giman 1b03e61d2b feat: add login by Google and GitHub 2026-01-11 23:22:30 +01:00
38 changed files with 2591 additions and 4763 deletions
+4 -1
View File
@@ -36,4 +36,7 @@ customer-license
!build-dockers.sh
!build-docker-api.sh
!build-docker-web.sh
!entrypoint.sh
!entrypoint.sh
!build-docker-migrations.sh
*.private
*.private*
+8
View File
@@ -19,6 +19,7 @@
"@rollup/plugin-commonjs": "^28.0.1",
"@rollup/plugin-node-resolve": "^15.3.0",
"@types/bcrypt": "^5.0.2",
"@types/cookie-parser": "^1.4.10",
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.7",
@@ -39,10 +40,14 @@
},
"dependencies": {
"@types/bcryptjs": "^2.4.6",
"@types/passport": "^1.0.17",
"@types/passport-github2": "^1.2.9",
"@types/passport-google-oauth20": "^2.0.17",
"@vitejs/plugin-legacy": "^5.4.2",
"@vitejs/plugin-vue": "^5.1.4",
"axios": "^1.7.7",
"bcryptjs": "^2.4.3",
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"drizzle-orm": "^0.44.4",
@@ -50,6 +55,9 @@
"express": "4.21.0",
"helmet": "^7.1.0",
"jsonwebtoken": "^9.0.2",
"passport": "^0.7.0",
"passport-github2": "^0.1.12",
"passport-google-oauth20": "^2.0.0",
"pg": "^8.16.3",
"pino": "^9.4.0",
"rotating-file-stream": "^3.2.5",
@@ -0,0 +1,5 @@
#!/bin/bash
VERSION=$1
docker buildx build --platform=linux/amd64,linux/arm64 -t gimanhead/taskview-ce-db-migration:$VERSION -t gimanhead/taskview-ce-db-migration:latest . --load
+31 -3
View File
@@ -4,8 +4,18 @@ 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';
import cookieParser from 'cookie-parser';
const VRS = '1.18.0';
const allow = new Set([
...(process.env.CORS_REMOVE_DEFAULT_ALLOWED_ORIGINS === 'true' ? [] : [
// default allowed origins for official TaskView apps
"https://app.taskview.tech",
"https://taskview.handscream.com",
"capacitor://taskview.handscream.com",
"capacitor://app.taskview.tech",
]),
]);
export default class App {
public app: express.Application;
@@ -15,11 +25,20 @@ export default class App {
this.app = express();
this.port = port;
this.extendApp();
this.initializeMiddlewares();
this.initializeRoutes();
this.app.use(errorHandler);
this.app.use(passport.initialize());
initPassportLogin();
this.extendMiddlewares();
}
protected extendApp(): void { }
protected extendMiddlewares(): void { }
private initializeMiddlewares() {
//add tvJson method, clien need response format like {response: data}
this.app.use((_req: Request, res: Response, next) => {
@@ -29,8 +48,18 @@ export default class App {
next();
});
this.app.use(cookieParser());
this.app.use(appUserMiddleware);
this.app.use(cors());
this.app.use(cors({
credentials: true,
origin(origin, cb) {
if (!origin) return cb(null, true);
if (allow.has(origin)) return cb(null, true);
return cb(new Error(`CORS blocked origin: ${origin}`), false);
},
}));
this.app.use(helmet());
this.app.use(express.json());
this.app.use(express.urlencoded({ extended: true }));
@@ -45,7 +74,6 @@ export default class App {
public listen() {
return this.app.listen(this.port, '0.0.0.0', () => {
console.log(`Server is running on port ${this.port}`);
console.log(`Server version is ${VRS}`);
});
}
}
+124 -22
View File
@@ -16,12 +16,15 @@ 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;
private readonly jwtExp: string = process.env.ACCESS_LIFE_TIME!;
private readonly jwtRefreshExp: string = process.env.REFRESH_LIFE_TIME!;
private readonly refreshTokenCookieName: string = 'taskview-refresh';
comparePasswords(pwd: string, hash: string): Promise<boolean> {
return new Promise((resolve) => {
//Prev version was written in PHP need to replace
@@ -91,6 +94,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 +116,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 +181,86 @@ 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}`);
}
setRefreshToken = async (res: Response, refreshToken: string) => {
res.cookie(this.refreshTokenCookieName, refreshToken, {
httpOnly: true,
secure: true,
sameSite: "none",
maxAge: 1000 * 60 * 60 * 24 * 30,
});
}
loginByCode = async (req: Request, res: Response) => {
const schema = z.object({
email: z.string().email().toLowerCase(),
@@ -202,7 +288,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 +316,9 @@ 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);
await this.setRefreshToken(res, tokens.refresh);
return res.json(tokens);
};
@@ -276,6 +365,8 @@ export default class AuthController {
$logger.error(`Can not update tokens in JWT Storage for user ${userData.id} and rowId ${tokenRowId}`);
}
await this.setRefreshToken(res, tokens.refresh);
return res.json(tokens);
}
@@ -473,37 +564,46 @@ export default class AuthController {
};
logout = async (req: Request, res: Response) => {
if (!req.headers['authorization']) {
return res.status(400).end();
}
this.setRefreshToken(res, '');
const result = req.headers['authorization'].match(/Bearer\s(\S+)/);
const result = req.headers['authorization']?.match(/Bearer\s(\S+)/);
if (!result) {
return res.status(400).end();
return res.status(401).send({ message: 'Unauthorized' });
}
const payload = decode(result['1']) as UserJwtPayload;
if (!payload) {
return res.status(400).end();
const tokenId = req.appUser.getTokenId();
if (!tokenId) {
return res.status(401).send({ message: 'Unauthorized' });
}
const deleteResult = await req.appUser.authManager.jwtStorage.deleteTokens(payload.userData.id, result['1']);
const deleteResult = await req.appUser.authManager.jwtStorage.deleteTokens(tokenId, result['1']);
if (!deleteResult) {
return res.status(500).end();
return res.status(500).send({ message: 'Failed to revoke token' });
}
return res.send();
return res.status(204).end();
};
refreshTokens = async (req: Request, res: Response) => {
const refreshData = RefreshTokenSchema.safeParse(req.body);
if (!refreshData.success) {
return res.status(400).end();
let refreshToken = req.cookies[this.refreshTokenCookieName];
if (!refreshToken) {
const refreshData = RefreshTokenSchema.safeParse(req.body);
if (!refreshData.success) {
return res.status(400).send({ message: 'Invalid refresh token' });
}
refreshToken = refreshData.data.refreshToken;
$logger.info(`Refresh token found in body`);
} else {
$logger.info(`Refresh token found in cookies`);
}
const payload = await AuthController.validateTokens(refreshData.data.refreshToken);
const payload = await AuthController.validateTokens(refreshToken);
if (!payload) {
return res.status(400).end();
@@ -522,6 +622,8 @@ export default class AuthController {
return res.status(500).end();
}
await this.setRefreshToken(res, newTokens.refresh);
return res.json(newTokens);
};
+1
View File
@@ -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;
+21 -2
View File
@@ -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
);
}
}
+7 -7
View File
@@ -20,11 +20,11 @@ export default class JwtStorage {
}
return false;
} catch (error: any) {
$logger.error('Can not complete initTokenRecord', {
$logger.error({
userId,
errorMessage: error.message,
errorStack: error.stack,
});
}, 'Can not complete initTokenRecord');
return false;
}
}
@@ -40,7 +40,7 @@ export default class JwtStorage {
const res = await this.db.query(query, [accessToken, refreshToken, rowId]);
return !!(res.rowCount && res.rowCount > 0);
} catch (error: any) {
$logger.error('Error updating tokens:', { errorMessage: error.message, errorStack: error.stack });
$logger.error({ errorMessage: error.message, errorStack: error.stack }, 'Error updating tokens:');
return false;
}
}
@@ -55,11 +55,11 @@ export default class JwtStorage {
return false;
} catch (error: any) {
$logger.error('Error fetching tokens', {
$logger.error({
rowId,
errorMessage: error.message,
errorStack: error.stack,
});
}, 'Error fetching tokens');
return false;
}
}
@@ -67,8 +67,8 @@ export default class JwtStorage {
async deleteTokens(userId: number, accessToken: string): Promise<boolean> {
try {
const query = 'DELETE FROM tv_auth.user_tokens WHERE user_id = $1 AND access_token = $2;';
const deleteResul = await this.db.query(query, [userId, accessToken]);
return !!(deleteResul.rowCount && deleteResul.rowCount > 0);
await this.db.query(query, [userId, accessToken]);
return true;
} catch (_error: any) {
return false;
}
@@ -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;
+30 -4
View File
@@ -1,19 +1,45 @@
#!/bin/bash
# This script builds the docker images for the taskview-ce-monorepo
# This script builds the docker images for TaskView Community Edition
# - taskview-ce-api-server
# - taskview-ce-db-migration
# - taskview-ce-webapp
set -e
VERSION=$1
# If version not provided, read from package.json
if [ -z "$VERSION" ]; then
VERSION=$(node -p "require('./package.json').version")
fi
echo $VERSION
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
nvm use 24
node -v
echo "Building TaskView CE version: $VERSION"
# Build CE API
echo "========================================="
echo "Building CE API Server..."
echo "========================================="
cd api
bash build-docker-api.sh $VERSION
cd ..
# Build CE Web
echo "========================================="
echo "Building CE Web App..."
echo "========================================="
cd web
bash build-docker-web.sh $VERSION
cd ..
cd ..
echo "========================================="
echo "Build complete!"
echo "Images built:"
echo " - gimanhead/taskview-ce-api-server:$VERSION"
echo " - gimanhead/taskview-ce-webapp:$VERSION"
echo "========================================="
+8 -2
View File
@@ -1,6 +1,6 @@
{
"name": "taskview-ce-monorepo",
"version": "1.18.2",
"version": "1.19.6",
"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"
}
}
+1855 -4633
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -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 1196
versionName "1.19.6"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
+1
View File
@@ -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')
+3 -3
View File
@@ -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",
+11 -3
View 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"
@@ -6,6 +6,7 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:networkSecurityConfig="@xml/network_security_config"
android:theme="@style/AppTheme">
<activity
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation"
@@ -18,6 +19,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 +32,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>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
</network-security-config>
+9 -6
View File
@@ -1,18 +1,21 @@
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
include ':capacitor-android'
project(':capacitor-android').projectDir = new File('../../node_modules/.pnpm/@capacitor+android@7.4.4_@capacitor+core@7.4.4/node_modules/@capacitor/android/capacitor')
project(':capacitor-android').projectDir = new File('../../../node_modules/.pnpm/@capacitor+android@7.4.5_@capacitor+core@7.4.5/node_modules/@capacitor/android/capacitor')
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')
project(':capacitor-app').projectDir = new File('../../../node_modules/.pnpm/@capacitor+app@7.0.1_@capacitor+core@7.4.5/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.5/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')
project(':capacitor-device').projectDir = new File('../../../node_modules/.pnpm/@capacitor+device@7.0.1_@capacitor+core@7.4.5/node_modules/@capacitor/device/android')
include ':capacitor-preferences'
project(':capacitor-preferences').projectDir = new File('../../node_modules/.pnpm/@capacitor+preferences@7.0.1_@capacitor+core@7.4.4/node_modules/@capacitor/preferences/android')
project(':capacitor-preferences').projectDir = new File('../../../node_modules/.pnpm/@capacitor+preferences@7.0.1_@capacitor+core@7.4.5/node_modules/@capacitor/preferences/android')
include ':capacitor-splash-screen'
project(':capacitor-splash-screen').projectDir = new File('../../node_modules/.pnpm/@capacitor+splash-screen@7.0.3_@capacitor+core@7.4.4/node_modules/@capacitor/splash-screen/android')
project(':capacitor-splash-screen').projectDir = new File('../../../node_modules/.pnpm/@capacitor+splash-screen@7.0.4_@capacitor+core@7.4.5/node_modules/@capacitor/splash-screen/android')
include ':capgo-capacitor-updater'
project(':capgo-capacitor-updater').projectDir = new File('../../node_modules/.pnpm/@capgo+capacitor-updater@7.29.0_@capacitor+core@7.4.4/node_modules/@capgo/capacitor-updater/android')
project(':capgo-capacitor-updater').projectDir = new File('../../../node_modules/.pnpm/@capgo+capacitor-updater@7.41.1_@capacitor+core@7.4.5/node_modules/@capgo/capacitor-updater/android')
+10 -9
View File
@@ -12,18 +12,19 @@ const config: CapacitorConfig = {
zoomEnabled: false,
},
server: {
hostname: 'taskview.handscream.com',
//androidScheme: 'https://',
hostname: 'app.taskview.tech',
},
plugins: {
"CapacitorUpdater": {
"autoUpdate": false,
}
CapacitorUpdater: {
autoUpdate: false,
},
CapacitorCookies: {
enabled: true,
},
CapacitorHttp: {
enabled: true,
},
},
// server: {
// url: 'http://192.168.0.2:3000',
// cleartext: true,
// },
};
export default config;
+4 -4
View File
@@ -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.6;
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.6;
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.6;
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.6;
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>
+9 -8
View File
@@ -1,4 +1,4 @@
require_relative '../../../node_modules/.pnpm/@capacitor+ios@7.4.4_@capacitor+core@7.4.4/node_modules/@capacitor/ios/scripts/pods_helpers'
require_relative '../../../../node_modules/.pnpm/@capacitor+ios@7.4.5_@capacitor+core@7.4.5/node_modules/@capacitor/ios/scripts/pods_helpers'
platform :ios, '14.0'
use_frameworks!
@@ -9,13 +9,14 @@ use_frameworks!
install! 'cocoapods', :disable_input_output_paths => true
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 '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'
pod 'CapgoCapacitorUpdater', :path => '../../../node_modules/.pnpm/@capgo+capacitor-updater@7.29.0_@capacitor+core@7.4.4/node_modules/@capgo/capacitor-updater'
pod 'Capacitor', :path => '../../../../node_modules/.pnpm/@capacitor+ios@7.4.5_@capacitor+core@7.4.5/node_modules/@capacitor/ios'
pod 'CapacitorCordova', :path => '../../../../node_modules/.pnpm/@capacitor+ios@7.4.5_@capacitor+core@7.4.5/node_modules/@capacitor/ios'
pod 'CapacitorApp', :path => '../../../../node_modules/.pnpm/@capacitor+app@7.0.1_@capacitor+core@7.4.5/node_modules/@capacitor/app'
pod 'CapacitorBrowser', :path => '../../../../node_modules/.pnpm/@capacitor+browser@7.0.3_@capacitor+core@7.4.5/node_modules/@capacitor/browser'
pod 'CapacitorDevice', :path => '../../../../node_modules/.pnpm/@capacitor+device@7.0.1_@capacitor+core@7.4.5/node_modules/@capacitor/device'
pod 'CapacitorPreferences', :path => '../../../../node_modules/.pnpm/@capacitor+preferences@7.0.1_@capacitor+core@7.4.5/node_modules/@capacitor/preferences'
pod 'CapacitorSplashScreen', :path => '../../../../node_modules/.pnpm/@capacitor+splash-screen@7.0.4_@capacitor+core@7.4.5/node_modules/@capacitor/splash-screen'
pod 'CapgoCapacitorUpdater', :path => '../../../../node_modules/.pnpm/@capgo+capacitor-updater@7.41.1_@capacitor+core@7.4.5/node_modules/@capgo/capacitor-updater'
end
target 'TaskView' do
+33 -27
View File
@@ -1,71 +1,77 @@
PODS:
- Alamofire (5.10.2)
- BigInt (5.2.0)
- Capacitor (7.4.4):
- Capacitor (7.4.5):
- CapacitorCordova
- CapacitorApp (7.0.1):
- Capacitor
- CapacitorCordova (7.4.4)
- CapacitorBrowser (7.0.3):
- Capacitor
- CapacitorCordova (7.4.5)
- CapacitorDevice (7.0.1):
- Capacitor
- CapacitorPreferences (7.0.1):
- Capacitor
- CapacitorSplashScreen (7.0.3):
- CapacitorSplashScreen (7.0.4):
- Capacitor
- CapgoCapacitorUpdater (7.29.0):
- CapgoCapacitorUpdater (7.41.1):
- Alamofire (= 5.10.2)
- BigInt (= 5.2.0)
- Capacitor
- SSZipArchive (= 2.4.3)
- Version (= 0.8.0)
- SSZipArchive (2.4.3)
- ZIPFoundation (~> 0.9)
- Version (0.8.0)
- ZIPFoundation (0.9.20)
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`)"
- "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`)"
- "CapacitorSplashScreen (from `../../../node_modules/.pnpm/@capacitor+splash-screen@7.0.3_@capacitor+core@7.4.4/node_modules/@capacitor/splash-screen`)"
- "CapgoCapacitorUpdater (from `../../../node_modules/.pnpm/@capgo+capacitor-updater@7.29.0_@capacitor+core@7.4.4/node_modules/@capgo/capacitor-updater`)"
- "Capacitor (from `../../../../node_modules/.pnpm/@capacitor+ios@7.4.5_@capacitor+core@7.4.5/node_modules/@capacitor/ios`)"
- "CapacitorApp (from `../../../../node_modules/.pnpm/@capacitor+app@7.0.1_@capacitor+core@7.4.5/node_modules/@capacitor/app`)"
- "CapacitorBrowser (from `../../../../node_modules/.pnpm/@capacitor+browser@7.0.3_@capacitor+core@7.4.5/node_modules/@capacitor/browser`)"
- "CapacitorCordova (from `../../../../node_modules/.pnpm/@capacitor+ios@7.4.5_@capacitor+core@7.4.5/node_modules/@capacitor/ios`)"
- "CapacitorDevice (from `../../../../node_modules/.pnpm/@capacitor+device@7.0.1_@capacitor+core@7.4.5/node_modules/@capacitor/device`)"
- "CapacitorPreferences (from `../../../../node_modules/.pnpm/@capacitor+preferences@7.0.1_@capacitor+core@7.4.5/node_modules/@capacitor/preferences`)"
- "CapacitorSplashScreen (from `../../../../node_modules/.pnpm/@capacitor+splash-screen@7.0.4_@capacitor+core@7.4.5/node_modules/@capacitor/splash-screen`)"
- "CapgoCapacitorUpdater (from `../../../../node_modules/.pnpm/@capgo+capacitor-updater@7.41.1_@capacitor+core@7.4.5/node_modules/@capgo/capacitor-updater`)"
SPEC REPOS:
trunk:
- Alamofire
- BigInt
- SSZipArchive
- Version
- ZIPFoundation
EXTERNAL SOURCES:
Capacitor:
:path: "../../../node_modules/.pnpm/@capacitor+ios@7.4.4_@capacitor+core@7.4.4/node_modules/@capacitor/ios"
:path: "../../../../node_modules/.pnpm/@capacitor+ios@7.4.5_@capacitor+core@7.4.5/node_modules/@capacitor/ios"
CapacitorApp:
:path: "../../../node_modules/.pnpm/@capacitor+app@7.0.1_@capacitor+core@7.4.4/node_modules/@capacitor/app"
:path: "../../../../node_modules/.pnpm/@capacitor+app@7.0.1_@capacitor+core@7.4.5/node_modules/@capacitor/app"
CapacitorBrowser:
:path: "../../../../node_modules/.pnpm/@capacitor+browser@7.0.3_@capacitor+core@7.4.5/node_modules/@capacitor/browser"
CapacitorCordova:
:path: "../../../node_modules/.pnpm/@capacitor+ios@7.4.4_@capacitor+core@7.4.4/node_modules/@capacitor/ios"
:path: "../../../../node_modules/.pnpm/@capacitor+ios@7.4.5_@capacitor+core@7.4.5/node_modules/@capacitor/ios"
CapacitorDevice:
:path: "../../../node_modules/.pnpm/@capacitor+device@7.0.1_@capacitor+core@7.4.4/node_modules/@capacitor/device"
:path: "../../../../node_modules/.pnpm/@capacitor+device@7.0.1_@capacitor+core@7.4.5/node_modules/@capacitor/device"
CapacitorPreferences:
:path: "../../../node_modules/.pnpm/@capacitor+preferences@7.0.1_@capacitor+core@7.4.4/node_modules/@capacitor/preferences"
:path: "../../../../node_modules/.pnpm/@capacitor+preferences@7.0.1_@capacitor+core@7.4.5/node_modules/@capacitor/preferences"
CapacitorSplashScreen:
:path: "../../../node_modules/.pnpm/@capacitor+splash-screen@7.0.3_@capacitor+core@7.4.4/node_modules/@capacitor/splash-screen"
:path: "../../../../node_modules/.pnpm/@capacitor+splash-screen@7.0.4_@capacitor+core@7.4.5/node_modules/@capacitor/splash-screen"
CapgoCapacitorUpdater:
:path: "../../../node_modules/.pnpm/@capgo+capacitor-updater@7.29.0_@capacitor+core@7.4.4/node_modules/@capgo/capacitor-updater"
:path: "../../../../node_modules/.pnpm/@capgo+capacitor-updater@7.41.1_@capacitor+core@7.4.5/node_modules/@capgo/capacitor-updater"
SPEC CHECKSUMS:
Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496
BigInt: f668a80089607f521586bbe29513d708491ef2f7
Capacitor: 09d9ff8e9618e8c4b3cab2bbee34a17215dd2fef
Capacitor: a5bf59e09f9dd82694fdcca4d107b4d215ac470f
CapacitorApp: d63334c052278caf5d81585d80b21905c6f93f39
CapacitorCordova: bf648a636f3c153f652d312ae145fb508b6ffced
CapacitorBrowser: 66aa8ff09cdca2a327ce464b113b470e6f667753
CapacitorCordova: 31bbe4466000c6b86d9b7f1181ee286cff0205aa
CapacitorDevice: fe3f190e1d718f4607bdc6b73993433d1c84f409
CapacitorPreferences: cbf154e5e5519b7f5ab33817a334dda1e98387f9
CapacitorSplashScreen: d06ae8804808e9f649a08e7bb7f283c77b688084
CapgoCapacitorUpdater: b3a419b281c01eb9efc8969fe5183ea0da636d25
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
CapacitorSplashScreen: 03d04ffe57d5b619cfc162efc7415a965c92cc3f
CapgoCapacitorUpdater: 4bc3381d413a3e36ecfee9a548b19cc3f4b2a9c8
Version: de5907f2c5d0f3cf21708db7801d1d5401139486
ZIPFoundation: dfd3d681c4053ff7e2f7350bc4e53b5dba3f5351
PODFILE CHECKSUM: 65fabdcdf017e31d75062b30247956106ea17004
PODFILE CHECKSUM: d0ad3405654a881e2cb6b93743d30c0ace507415
COCOAPODS: 1.16.2
+2 -1
View File
@@ -1,6 +1,6 @@
{
"name": "taskview-ce-webapp",
"version": "1.17.0",
"version": "1.19.6",
"private": true,
"scripts": {
"dev": "vite",
@@ -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>
+5 -3
View File
@@ -79,9 +79,11 @@ onMounted(async () => {
});
async function logoutFromApp() {
await logout();
await router.push('/');
router.go(0);
const result = await logout();
if (result) {
await router.push('/');
router.go(0);
}
}
async function checkForUpdates() {
+8 -2
View File
@@ -3,8 +3,14 @@ import $api from '@/helpers/axios';
import { $ls } from '@/plugins/axios';
export async function logout() {
await $api.post<LogoutResponse>('/module/auth/logout').catch((err) => {
const result = await $api.post<LogoutResponse>('/module/auth/logout').catch((err) => {
console.log(err, $api);
});
await $ls.invalidateTokens();
if (result) {
await $ls.invalidateTokens();
return true;
}
return false;
}
+2
View File
@@ -2,7 +2,9 @@ import axios from 'axios';
import { useTaskViewMainUrl } from '@/composition/useTaskViewMainUrl';
const $api = axios.create({
withCredentials: true,
baseURL: useTaskViewMainUrl(),
timeout: 10000,
});
export default $api;
-14
View File
@@ -1,14 +0,0 @@
import { defineComponent } from 'vue';
import { AppCredentialsForm } from '@/components/Authentication/AppCredentialsForm';
export default defineComponent({
components: {
AppCredentialsForm,
},
data() {
return {
msg: 'login',
};
},
});
+72 -2
View File
@@ -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>
-3
View File
@@ -1,3 +0,0 @@
import LoginPage from '@/pages/LoginPage/LoginPage.vue';
export default LoginPage;
+1 -1
View File
@@ -67,7 +67,7 @@ const router = createRouter({
{
path: '/login',
name: 'login',
component: () => import('@/pages/LoginPage'),
component: () => import('@/pages/LoginPage/LoginPage.vue'),
children: [],
},
],