mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-03-07 20:10:03 +03:00
Compare commits
1 Commits
100e6f8174
...
add-settin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
010f0fbb1c |
@@ -678,22 +678,6 @@ paths:
|
||||
$ref: '#/components/schemas/SuccessObject'
|
||||
security:
|
||||
- Auth query parameter: []
|
||||
/api/isPinSet:
|
||||
post:
|
||||
tags:
|
||||
- security
|
||||
summary: Check if pin is set
|
||||
description: Checks if the pin is set for settings
|
||||
operationId: post-api-isPinSet
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/inline_response_200_15'
|
||||
security:
|
||||
- Auth query parameter: []
|
||||
/api/generateNewAPIKey:
|
||||
post:
|
||||
tags:
|
||||
@@ -1311,6 +1295,48 @@ paths:
|
||||
- Auth query parameter: []
|
||||
tags:
|
||||
- multi-user mode
|
||||
/api/setPin:
|
||||
post:
|
||||
summary: Set settings pin
|
||||
operationId: post-api-setPin
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessObject'
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SetPinRequest'
|
||||
description: 'Sets a pin for the settings'
|
||||
security:
|
||||
- Auth query parameter: []
|
||||
tags:
|
||||
- security
|
||||
/api/auth/pinLogin:
|
||||
post:
|
||||
summary: Pin login
|
||||
operationId: post-api-pin-login
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PinLoginResponse'
|
||||
description: Use this endpoint to generate a JWT token for pin authentication. Put anything in the username field.
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/LoginRequest'
|
||||
security:
|
||||
- Auth query parameter: []
|
||||
tags:
|
||||
- security
|
||||
/api/getUsers:
|
||||
post:
|
||||
summary: Get all users
|
||||
@@ -3025,6 +3051,13 @@ components:
|
||||
type: string
|
||||
required:
|
||||
- role
|
||||
SetPinRequest:
|
||||
required:
|
||||
- new_pin
|
||||
type: object
|
||||
properties:
|
||||
new_pin:
|
||||
type: string
|
||||
file:
|
||||
title: file
|
||||
type: object
|
||||
@@ -3074,6 +3107,13 @@ components:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/UserPermission'
|
||||
PinLoginResponse:
|
||||
required:
|
||||
- pin_token
|
||||
type: object
|
||||
properties:
|
||||
pin_token:
|
||||
type: string
|
||||
UpdateUserRequest:
|
||||
required:
|
||||
- change_object
|
||||
|
||||
@@ -742,6 +742,18 @@ const optionalJwt = async function (req, res, next) {
|
||||
return next();
|
||||
};
|
||||
|
||||
const optionalPin = async function (req, res, next) {
|
||||
const use_pin = config_api.getConfigItem('ytdl_use_pin');
|
||||
if (use_pin && req.path.includes('/api/setConfig')) {
|
||||
if (!req.query.pin_token) {
|
||||
res.sendStatus(418); // I'm a teapot (RFC 2324)
|
||||
return;
|
||||
}
|
||||
return next();
|
||||
}
|
||||
return next();
|
||||
};
|
||||
|
||||
app.get('/api/config', function(req, res) {
|
||||
let config_file = config_api.getConfigFile();
|
||||
res.send({
|
||||
@@ -750,7 +762,7 @@ app.get('/api/config', function(req, res) {
|
||||
});
|
||||
});
|
||||
|
||||
app.post('/api/setConfig', optionalJwt, function(req, res) {
|
||||
app.post('/api/setConfig', optionalJwt, optionalPin, function(req, res) {
|
||||
let new_config_file = req.body.new_config_file;
|
||||
if (new_config_file && new_config_file['YoutubeDLMaterial']) {
|
||||
let success = config_api.setConfigFile(new_config_file);
|
||||
@@ -1934,12 +1946,23 @@ app.post('/api/auth/login'
|
||||
, auth_api.generateJWT
|
||||
, auth_api.returnAuthResponse
|
||||
);
|
||||
app.post('/api/auth/pinLogin'
|
||||
, auth_api.passport.authenticate(['local_pin'], {})
|
||||
, auth_api.generatePinJWT
|
||||
, auth_api.returnPinAuthResponse
|
||||
);
|
||||
app.post('/api/auth/jwtAuth'
|
||||
, auth_api.passport.authenticate('jwt', { session: false })
|
||||
, auth_api.passport.authorize('jwt')
|
||||
, auth_api.generateJWT
|
||||
, auth_api.returnAuthResponse
|
||||
);
|
||||
app.post('/api/auth/pinAuth'
|
||||
, auth_api.passport.authenticate('pin', { session: false })
|
||||
, auth_api.passport.authorize('pin')
|
||||
, auth_api.generatePinJWT
|
||||
, auth_api.returnPinAuthResponse
|
||||
);
|
||||
app.post('/api/auth/changePassword', optionalJwt, async (req, res) => {
|
||||
let user_uid = req.body.user_uid;
|
||||
let password = req.body.new_password;
|
||||
@@ -2029,6 +2052,13 @@ app.post('/api/changeRolePermissions', optionalJwt, async (req, res) => {
|
||||
res.send({success: success});
|
||||
});
|
||||
|
||||
app.post('/api/setPin', function(req, res) {
|
||||
const success = auth_api.setPin(req.body.new_pin);
|
||||
res.send({
|
||||
success: success
|
||||
});
|
||||
});
|
||||
|
||||
// notifications
|
||||
|
||||
app.post('/api/getNotifications', optionalJwt, async (req, res) => {
|
||||
|
||||
@@ -15,7 +15,6 @@ var JwtStrategy = require('passport-jwt').Strategy,
|
||||
// other required vars
|
||||
let SERVER_SECRET = null;
|
||||
let JWT_EXPIRATION = null;
|
||||
let opts = null;
|
||||
let saltRounds = null;
|
||||
|
||||
exports.initialize = function () {
|
||||
@@ -50,11 +49,11 @@ exports.initialize = function () {
|
||||
db_api.users_db.set('jwt_secret', SERVER_SECRET).write();
|
||||
}
|
||||
|
||||
opts = {}
|
||||
const opts = {}
|
||||
opts.jwtFromRequest = ExtractJwt.fromUrlQueryParameter('jwt');
|
||||
opts.secretOrKey = SERVER_SECRET;
|
||||
|
||||
exports.passport.use(new JwtStrategy(opts, async function(jwt_payload, done) {
|
||||
exports.passport.use('jwt', new JwtStrategy(opts, async function(jwt_payload, done) {
|
||||
const user = await db_api.getRecord('users', {uid: jwt_payload.user});
|
||||
if (user) {
|
||||
return done(null, user);
|
||||
@@ -63,6 +62,21 @@ exports.initialize = function () {
|
||||
// or you could create a new account
|
||||
}
|
||||
}));
|
||||
|
||||
const pin_opts = {}
|
||||
pin_opts.jwtFromRequest = ExtractJwt.fromUrlQueryParameter('pin_token');
|
||||
pin_opts.secretOrKey = SERVER_SECRET;
|
||||
|
||||
exports.passport.use('pin', new JwtStrategy(pin_opts, {
|
||||
passwordField: 'pin'},
|
||||
async function(username, password, done) {
|
||||
if (await bcrypt.compare(password, config_api.getConfigItem('ytdl_pin_hash'))) {
|
||||
return done(null, { success: true });
|
||||
} else {
|
||||
return done(null, false, { message: 'Incorrect pin' });
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
const setupRoles = async () => {
|
||||
@@ -188,6 +202,10 @@ exports.login = async (username, password) => {
|
||||
return await bcrypt.compare(password, user.passhash) ? user : false;
|
||||
}
|
||||
|
||||
exports.pinLogin = async (pin) => {
|
||||
return await bcrypt.compare(pin, config_api.getConfigItem('ytdl_pin_hash'));
|
||||
}
|
||||
|
||||
exports.passport.use(new LocalStrategy({
|
||||
usernameField: 'username',
|
||||
passwordField: 'password'},
|
||||
@@ -196,6 +214,14 @@ exports.passport.use(new LocalStrategy({
|
||||
}
|
||||
));
|
||||
|
||||
exports.passport.use('local_pin', new LocalStrategy({
|
||||
usernameField: 'username',
|
||||
passwordField: 'password'},
|
||||
async function(username, password, done) {
|
||||
return done(null, await exports.pinLogin(password));
|
||||
}
|
||||
));
|
||||
|
||||
var getLDAPConfiguration = function(req, callback) {
|
||||
const ldap_config = config_api.getConfigItem('ytdl_ldap_config');
|
||||
const opts = {server: ldap_config};
|
||||
@@ -237,6 +263,14 @@ exports.generateJWT = function(req, res, next) {
|
||||
next();
|
||||
}
|
||||
|
||||
exports.generatePinJWT = function(req, res, next) {
|
||||
var payload = {
|
||||
exp: Math.floor(Date.now() / 1000) + JWT_EXPIRATION
|
||||
};
|
||||
req.token = jwt.sign(payload, SERVER_SECRET);
|
||||
next();
|
||||
}
|
||||
|
||||
exports.returnAuthResponse = async function(req, res) {
|
||||
res.status(200).json({
|
||||
user: req.user,
|
||||
@@ -246,6 +280,12 @@ exports.returnAuthResponse = async function(req, res) {
|
||||
});
|
||||
}
|
||||
|
||||
exports.returnPinAuthResponse = async function(req, res) {
|
||||
res.status(200).json({
|
||||
pin_token: req.token
|
||||
});
|
||||
}
|
||||
|
||||
/***************************************
|
||||
* Authorization: middleware that checks the
|
||||
* JWT token for validity before allowing
|
||||
@@ -439,6 +479,13 @@ exports.userPermissions = async function(user_uid) {
|
||||
return user_permissions;
|
||||
}
|
||||
|
||||
// pin
|
||||
|
||||
exports.setPin = async (new_pin) => {
|
||||
const pin_hash = await bcrypt.hash(new_pin, saltRounds);
|
||||
return config_api.setConfigItem('ytdl_pin_hash', pin_hash);
|
||||
}
|
||||
|
||||
function getToken(queryParams) {
|
||||
if (queryParams && queryParams.jwt) {
|
||||
var parted = queryParams.jwt.split(' ');
|
||||
@@ -450,7 +497,7 @@ function getToken(queryParams) {
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function generateUserObject(userid, username, hash, auth_method = 'internal') {
|
||||
let new_user = {
|
||||
|
||||
@@ -202,6 +202,8 @@ const DEFAULT_CONFIG = {
|
||||
"enable_all_notifications": true,
|
||||
"allowed_notification_types": [],
|
||||
"enable_rss_feed": false,
|
||||
"use_pin": false,
|
||||
"pin_hash": "",
|
||||
},
|
||||
"API": {
|
||||
"use_API_key": false,
|
||||
|
||||
@@ -92,6 +92,14 @@ exports.CONFIG_ITEMS = {
|
||||
'key': 'ytdl_enable_rss_feed',
|
||||
'path': 'YoutubeDLMaterial.Extra.enable_rss_feed'
|
||||
},
|
||||
'ytdl_use_pin': {
|
||||
'key': 'ytdl_use_pin',
|
||||
'path': 'YoutubeDLMaterial.Extra.use_pin'
|
||||
},
|
||||
'ytdl_pin_hash': {
|
||||
'key': 'ytdl_pin_hash',
|
||||
'path': 'YoutubeDLMaterial.Extra.pin_hash'
|
||||
},
|
||||
|
||||
// API
|
||||
'ytdl_use_api_key': {
|
||||
|
||||
@@ -87,6 +87,7 @@ export type { LoginResponse } from './models/LoginResponse';
|
||||
export type { Notification } from './models/Notification';
|
||||
export { NotificationAction } from './models/NotificationAction';
|
||||
export { NotificationType } from './models/NotificationType';
|
||||
export type { PinLoginResponse } from './models/PinLoginResponse';
|
||||
export type { Playlist } from './models/Playlist';
|
||||
export type { RegisterRequest } from './models/RegisterRequest';
|
||||
export type { RegisterResponse } from './models/RegisterResponse';
|
||||
@@ -95,6 +96,7 @@ export type { RestoreDBBackupRequest } from './models/RestoreDBBackupRequest';
|
||||
export { Schedule } from './models/Schedule';
|
||||
export type { SetConfigRequest } from './models/SetConfigRequest';
|
||||
export type { SetNotificationsToReadRequest } from './models/SetNotificationsToReadRequest';
|
||||
export type { SetPinRequest } from './models/SetPinRequest';
|
||||
export type { SharingToggle } from './models/SharingToggle';
|
||||
export type { Sort } from './models/Sort';
|
||||
export type { SubscribeRequest } from './models/SubscribeRequest';
|
||||
|
||||
7
src/api-types/models/PinLoginResponse.ts
Normal file
7
src/api-types/models/PinLoginResponse.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type PinLoginResponse = {
|
||||
pin_token: string;
|
||||
};
|
||||
7
src/api-types/models/SetPinRequest.ts
Normal file
7
src/api-types/models/SetPinRequest.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type SetPinRequest = {
|
||||
new_pin: string;
|
||||
};
|
||||
@@ -51,7 +51,7 @@
|
||||
<a *ngIf="postsService.config && postsService.hasPermission('tasks_manager')" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/tasks'><ng-container i18n="Navigation menu Tasks Page title">Tasks</ng-container></a>
|
||||
<ng-container *ngIf="postsService.config && postsService.hasPermission('settings')">
|
||||
<mat-divider></mat-divider>
|
||||
<a mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/settings'><ng-container i18n="Settings menu label">Settings</ng-container></a>
|
||||
<a mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null; pinConfirm('/settings')" [routerLink]="!postsService.config.Extra.use_pin ? '/settings' : null"><ng-container i18n="Settings menu label">Settings</ng-container></a>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="postsService.config && allowSubscriptions && postsService.subscriptions && postsService.hasPermission('subscriptions')">
|
||||
<mat-divider *ngIf="postsService.subscriptions.length > 0"></mat-divider>
|
||||
|
||||
@@ -22,6 +22,7 @@ import { UserProfileDialogComponent } from './dialogs/user-profile-dialog/user-p
|
||||
import { SetDefaultAdminDialogComponent } from './dialogs/set-default-admin-dialog/set-default-admin-dialog.component';
|
||||
import { NotificationsComponent } from './components/notifications/notifications.component';
|
||||
import { ArchiveViewerComponent } from './components/archive-viewer/archive-viewer.component';
|
||||
import { PinLoginComponent } from './dialogs/pin-login-dialog/pin-login-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@@ -214,6 +215,16 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||
});
|
||||
}
|
||||
|
||||
pinConfirm(route: string): void {
|
||||
if (!this.postsService.config.Extra.use_pin) return;
|
||||
const dialogRef = this.dialog.open(PinLoginComponent);
|
||||
dialogRef.afterClosed().subscribe(success => {
|
||||
if (success) {
|
||||
this.router.navigate([route]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
notificationCountUpdate(new_count: number): void {
|
||||
this.notification_count = new_count;
|
||||
}
|
||||
|
||||
@@ -95,6 +95,8 @@ import { GenerateRssUrlComponent } from './dialogs/generate-rss-url/generate-rss
|
||||
import { SortPropertyComponent } from './components/sort-property/sort-property.component';
|
||||
import { OnlyNumberDirective } from './directives/only-number.directive';
|
||||
import { ArchiveViewerComponent } from './components/archive-viewer/archive-viewer.component';
|
||||
import { SetPinDialogComponent } from './dialogs/set-pin-dialog/set-pin-dialog.component';
|
||||
import { PinLoginComponent } from './dialogs/pin-login-dialog/pin-login-dialog.component';
|
||||
|
||||
registerLocaleData(es, 'es');
|
||||
|
||||
@@ -147,7 +149,9 @@ registerLocaleData(es, 'es');
|
||||
GenerateRssUrlComponent,
|
||||
SortPropertyComponent,
|
||||
OnlyNumberDirective,
|
||||
ArchiveViewerComponent
|
||||
ArchiveViewerComponent,
|
||||
SetPinDialogComponent,
|
||||
PinLoginComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<h4 mat-dialog-title i18n="Pin required">Pin required</h4>
|
||||
|
||||
<mat-dialog-content>
|
||||
<mat-form-field color="accent">
|
||||
<mat-label i18n="Enter pin">Enter pin</mat-label>
|
||||
<input [(ngModel)]="pin" matInput onlyNumber required type="password">
|
||||
</mat-form-field>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
<button mat-button mat-dialog-close i18n="Cancel">Cancel</button>
|
||||
<button mat-button [disabled]="!pin" (click)="pinLogin()"><ng-container i18n="Enter pin button">Enter pin</ng-container></button>
|
||||
<div class="mat-spinner" *ngIf="enterClicked">
|
||||
<mat-spinner [diameter]="25"></mat-spinner>
|
||||
</div>
|
||||
</mat-dialog-actions>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PinLoginComponent } from './pin-login-dialog.component';
|
||||
|
||||
describe('PinLoginComponent', () => {
|
||||
let component: PinLoginComponent;
|
||||
let fixture: ComponentFixture<PinLoginComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PinLoginComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(PinLoginComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { MatDialogRef } from '@angular/material/dialog';
|
||||
import { PostsService } from 'app/posts.services';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pin-login',
|
||||
templateUrl: './pin-login-dialog.component.html',
|
||||
styleUrls: ['./pin-login-dialog.component.scss']
|
||||
})
|
||||
export class PinLoginComponent {
|
||||
pin: string;
|
||||
enterClicked = false;
|
||||
|
||||
constructor(private postsService: PostsService, private dialogRef: MatDialogRef<PinLoginComponent>) {
|
||||
}
|
||||
|
||||
pinLogin() {
|
||||
this.enterClicked = true;
|
||||
this.postsService.pinLogin(this.pin).subscribe(res => {
|
||||
this.enterClicked = false;
|
||||
if (!res['pin_token']) {
|
||||
this.postsService.openSnackBar($localize`Pin failed!`);
|
||||
} else {
|
||||
this.postsService.httpOptions.params = this.postsService.httpOptions.params.set('pin_token', res['pin_token']);
|
||||
}
|
||||
this.dialogRef.close(res['pin_token']);
|
||||
}, err => {
|
||||
this.enterClicked = false;
|
||||
this.postsService.openSnackBar($localize`Pin failed!`);
|
||||
console.error(err);
|
||||
this.dialogRef.close(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
13
src/app/dialogs/set-pin-dialog/set-pin-dialog.component.html
Normal file
13
src/app/dialogs/set-pin-dialog/set-pin-dialog.component.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<h4 mat-dialog-title i18n="Set pin">Set pin</h4>
|
||||
|
||||
<mat-dialog-content>
|
||||
<mat-form-field color="accent">
|
||||
<mat-label i18n="Pin">Pin</mat-label>
|
||||
<input [(ngModel)]="pin" matInput onlyNumber required>
|
||||
</mat-form-field>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
<button mat-button mat-dialog-close i18n="Cancel">Cancel</button>
|
||||
<button mat-button [disabled]="!pin" (click)="setPin()"><ng-container i18n="Set pin button">Set pin</ng-container></button>
|
||||
</mat-dialog-actions>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SetPinDialogComponent } from './set-pin-dialog.component';
|
||||
|
||||
describe('SetPinDialogComponent', () => {
|
||||
let component: SetPinDialogComponent;
|
||||
let fixture: ComponentFixture<SetPinDialogComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ SetPinDialogComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SetPinDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
22
src/app/dialogs/set-pin-dialog/set-pin-dialog.component.ts
Normal file
22
src/app/dialogs/set-pin-dialog/set-pin-dialog.component.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { PostsService } from 'app/posts.services';
|
||||
|
||||
@Component({
|
||||
selector: 'app-set-pin-dialog',
|
||||
templateUrl: './set-pin-dialog.component.html',
|
||||
styleUrls: ['./set-pin-dialog.component.scss']
|
||||
})
|
||||
export class SetPinDialogComponent {
|
||||
pin: string;
|
||||
constructor(private postsService: PostsService) {
|
||||
|
||||
}
|
||||
|
||||
setPin() {
|
||||
this.postsService.setPin(this.pin).subscribe(res => {
|
||||
if (res['success']) {
|
||||
this.postsService.openSnackBar($localize`Successfully set pin!`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,8 @@ import {
|
||||
ImportArchiveRequest,
|
||||
Archive,
|
||||
Subscription,
|
||||
RestartDownloadResponse
|
||||
RestartDownloadResponse,
|
||||
PinLoginResponse
|
||||
} from '../api-types';
|
||||
import { isoLangs } from './settings/locales_list';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
@@ -734,7 +735,6 @@ export class PostsService implements CanActivate {
|
||||
return this.http.post<LoginResponse>(this.path + 'auth/login', body, this.httpOptions);
|
||||
}
|
||||
|
||||
// user methods
|
||||
jwtAuth() {
|
||||
const call = this.http.post(this.path + 'auth/jwtAuth', {}, this.httpOptions);
|
||||
call.subscribe(res => {
|
||||
@@ -752,6 +752,12 @@ export class PostsService implements CanActivate {
|
||||
return call;
|
||||
}
|
||||
|
||||
// pin methods
|
||||
pinLogin(pin: string) {
|
||||
const body: LoginRequest = {username: 'username', password: pin};
|
||||
return this.http.post<PinLoginResponse>(this.path + 'auth/pinLogin', body, this.httpOptions);
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.user = null;
|
||||
this.permissions = null;
|
||||
@@ -903,6 +909,11 @@ export class PostsService implements CanActivate {
|
||||
this.httpOptions);
|
||||
}
|
||||
|
||||
setPin(new_pin: string): Observable<SuccessObject> {
|
||||
return this.http.post<SuccessObject>(this.path + 'setPin', {new_pin: new_pin},
|
||||
this.httpOptions);
|
||||
}
|
||||
|
||||
public openSnackBar(message: string, action = ''): void {
|
||||
this.snackBar.open(message, action, {
|
||||
duration: 2000,
|
||||
|
||||
@@ -257,6 +257,25 @@
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<div *ngIf="new_config" class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-3">
|
||||
<mat-checkbox color="accent" [(ngModel)]="new_config['Extra']['use_pin']"><ng-container i18n="Use pin to hide settings setting">Use pin to hide settings</ng-container></mat-checkbox>
|
||||
</div>
|
||||
<div class="col-12 mb-3">
|
||||
<div class="pin-set" *ngIf="new_config['Extra']['pin_hash']">
|
||||
<mat-icon>done</mat-icon> <ng-container i18n="Pin set">Pin set!</ng-container>
|
||||
</div>
|
||||
<div>
|
||||
<button mat-stroked-button (click)="openSetPinDialog()">
|
||||
<ng-container *ngIf="!new_config['Extra']['pin_hash']" i18n="Set pin">Set pin</ng-container>
|
||||
<ng-container *ngIf="new_config['Extra']['pin_hash']" i18n="Reset pin">Reset pin</ng-container>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<div *ngIf="new_config" class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-3">
|
||||
|
||||
@@ -111,3 +111,9 @@
|
||||
top: 6px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.pin-set {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import { EditCategoryDialogComponent } from 'app/dialogs/edit-category-dialog/ed
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { Category, DBInfoResponse } from 'api-types';
|
||||
import { GenerateRssUrlComponent } from 'app/dialogs/generate-rss-url/generate-rss-url.component';
|
||||
import { SetPinDialogComponent } from 'app/dialogs/set-pin-dialog/set-pin-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-settings',
|
||||
@@ -373,4 +374,8 @@ export class SettingsComponent implements OnInit {
|
||||
maxWidth: '880px'
|
||||
});
|
||||
}
|
||||
|
||||
openSetPinDialog(): void {
|
||||
this.dialog.open(SetPinDialogComponent);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user