diff --git a/angular.json b/angular.json index 429e07d..1c5d172 100644 --- a/angular.json +++ b/angular.json @@ -1,175 +1,176 @@ { - "$schema": "./node_modules/@angular/cli/lib/config/schema.json", - "version": 1, - "newProjectRoot": "projects", - "projects": { - "unipollApp": { - "projectType": "application", - "schematics": { - "@schematics/angular:component": { - "style": "scss" + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "unipollApp": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + "outputPath": "dist/unipoll-app", + "index": "src/index.html", + "main": "src/main.ts", + "polyfills": [ + "zone.js" + ], + "tsConfig": "tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "src/favicon.ico", + "src/assets", + "src/config.json" + ], + "styles": [ + "@angular/material/prebuilt-themes/deeppurple-amber.css", + "src/styles.scss", + "node_modules/prismjs/themes/prism-okaidia.css", + "node_modules/prismjs/plugins/line-numbers/prism-line-numbers.css", + "node_modules/prismjs/plugins/command-line/prism-command-line.css", + "node_modules/katex/dist/katex.min.css" + ], + "scripts": [ + "node_modules/marked/marked.min.js", + "node_modules/prismjs/prism.js", + "node_modules/prismjs/plugins/highlight-keywords/prism-highlight-keywords.min.js", + "node_modules/prismjs/plugins/line-numbers/prism-line-numbers.js", + "node_modules/prismjs/plugins/command-line/prism-command-line.js", + "node_modules/prismjs/components/prism-css.min.js", + "node_modules/prismjs/components/prism-bash.min.js", + "node_modules/prismjs/components/prism-c.min.js", + "node_modules/prismjs/components/prism-cpp.min.js", + "node_modules/prismjs/components/prism-diff.min.js", + "node_modules/prismjs/components/prism-javascript.min.js", + "node_modules/prismjs/components/prism-latex.min.js", + "node_modules/prismjs/components/prism-markup.min.js", + "node_modules/prismjs/components/prism-markdown.min.js", + "node_modules/prismjs/components/prism-python.min.js", + "node_modules/prismjs/components/prism-typescript.min.js", + "node_modules/emoji-toolkit/lib/js/joypixels.min.js", + "node_modules/katex/dist/katex.min.js", + "node_modules/katex/dist/contrib/auto-render.min.js", + "node_modules/mermaid/dist/mermaid.min.js", + "node_modules/clipboard/dist/clipboard.min.js" + ] + }, + "configurations": { + "production": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.production.ts" } - }, - "root": "", - "sourceRoot": "src", - "prefix": "app", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:browser", - "options": { - "outputPath": "dist/unipoll-app", - "index": "src/index.html", - "main": "src/main.ts", - "polyfills": [ - "zone.js" - ], - "tsConfig": "tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "src/favicon.ico", - "src/assets" - ], - "styles": [ - "@angular/material/prebuilt-themes/deeppurple-amber.css", - "src/styles.scss", - "node_modules/prismjs/themes/prism-okaidia.css", - "node_modules/prismjs/plugins/line-numbers/prism-line-numbers.css", - "node_modules/prismjs/plugins/command-line/prism-command-line.css", - "node_modules/katex/dist/katex.min.css" - ], - "scripts": [ - "node_modules/marked/marked.min.js", - "node_modules/prismjs/prism.js", - "node_modules/prismjs/plugins/highlight-keywords/prism-highlight-keywords.min.js", - "node_modules/prismjs/plugins/line-numbers/prism-line-numbers.js", - "node_modules/prismjs/plugins/command-line/prism-command-line.js", - "node_modules/prismjs/components/prism-css.min.js", - "node_modules/prismjs/components/prism-bash.min.js", - "node_modules/prismjs/components/prism-c.min.js", - "node_modules/prismjs/components/prism-cpp.min.js", - "node_modules/prismjs/components/prism-diff.min.js", - "node_modules/prismjs/components/prism-javascript.min.js", - "node_modules/prismjs/components/prism-latex.min.js", - "node_modules/prismjs/components/prism-markup.min.js", - "node_modules/prismjs/components/prism-markdown.min.js", - "node_modules/prismjs/components/prism-python.min.js", - "node_modules/prismjs/components/prism-typescript.min.js", - "node_modules/emoji-toolkit/lib/js/joypixels.min.js", - "node_modules/katex/dist/katex.min.js", - "node_modules/katex/dist/contrib/auto-render.min.js", - "node_modules/mermaid/dist/mermaid.min.js", - "node_modules/clipboard/dist/clipboard.min.js" - ] - }, - "configurations": { - "production": { - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.production.ts" - } - ], - "budgets": [ - { - "type": "initial", - "maximumWarning": "10mb", - "maximumError": "20mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "8kb", - "maximumError": "10kb" - } - ], - "outputHashing": "all" - }, - "development": { - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.development.ts" - } - ], - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "builder": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "browserTarget": "unipollApp:build:production" - }, - "development": { - "browserTarget": "unipollApp:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "builder": "@angular-devkit/build-angular:extract-i18n", - "options": { - "browserTarget": "unipollApp:build" - } + ], + "budgets": [ + { + "type": "initial", + "maximumWarning": "10mb", + "maximumError": "20mb" }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "polyfills": [ - "zone.js", - "zone.js/testing" - ], - "tsConfig": "tsconfig.spec.json", - "inlineStyleLanguage": "scss", - "assets": [ - "src/favicon.ico", - "src/assets" - ], - "styles": [ - "@angular/material/prebuilt-themes/deeppurple-amber.css", - "src/styles.scss", - "node_modules/prismjs/themes/prism-okaidia.css", - "node_modules/prismjs/plugins/line-numbers/prism-line-numbers.css", - "node_modules/prismjs/plugins/command-line/prism-command-line.css", - "node_modules/katex/dist/katex.min.css" - ], - "scripts": [ - "node_modules/marked/marked.min.js", - "node_modules/prismjs/prism.js", - "node_modules/prismjs/plugins/highlight-keywords/prism-highlight-keywords.min.js", - "node_modules/prismjs/plugins/line-numbers/prism-line-numbers.js", - "node_modules/prismjs/plugins/command-line/prism-command-line.js", - "node_modules/prismjs/plugins/autoloader/prism-autoloader.min.js", - "node_modules/prismjs/components/prism-css.min.js", - "node_modules/prismjs/components/prism-bash.min.js", - "node_modules/prismjs/components/prism-c.min.js", - "node_modules/prismjs/components/prism-cpp.min.js", - "node_modules/prismjs/components/prism-diff.min.js", - "node_modules/prismjs/components/prism-javascript.min.js", - "node_modules/prismjs/components/prism-latex.min.js", - "node_modules/prismjs/components/prism-markup.min.js", - "node_modules/prismjs/components/prism-markdown.min.js", - "node_modules/prismjs/components/prism-python.min.js", - "node_modules/prismjs/components/prism-typescript.min.js", - "node_modules/emoji-toolkit/lib/js/joypixels.min.js", - "node_modules/katex/dist/katex.min.js", - "node_modules/katex/dist/contrib/auto-render.min.js", - "node_modules/mermaid/dist/mermaid.min.js", - "node_modules/clipboard/dist/clipboard.min.js" - ], - "karmaConfig": "karma.conf.js" - } + { + "type": "anyComponentStyle", + "maximumWarning": "8kb", + "maximumError": "10kb" } + ], + "outputHashing": "all" + }, + "development": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.development.ts" + } + ], + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "browserTarget": "unipollApp:build:production" + }, + "development": { + "browserTarget": "unipollApp:build:development" } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "unipollApp:build" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "polyfills": [ + "zone.js", + "zone.js/testing" + ], + "tsConfig": "tsconfig.spec.json", + "inlineStyleLanguage": "scss", + "assets": [ + "src/favicon.ico", + "src/assets" + ], + "styles": [ + "@angular/material/prebuilt-themes/deeppurple-amber.css", + "src/styles.scss", + "node_modules/prismjs/themes/prism-okaidia.css", + "node_modules/prismjs/plugins/line-numbers/prism-line-numbers.css", + "node_modules/prismjs/plugins/command-line/prism-command-line.css", + "node_modules/katex/dist/katex.min.css" + ], + "scripts": [ + "node_modules/marked/marked.min.js", + "node_modules/prismjs/prism.js", + "node_modules/prismjs/plugins/highlight-keywords/prism-highlight-keywords.min.js", + "node_modules/prismjs/plugins/line-numbers/prism-line-numbers.js", + "node_modules/prismjs/plugins/command-line/prism-command-line.js", + "node_modules/prismjs/plugins/autoloader/prism-autoloader.min.js", + "node_modules/prismjs/components/prism-css.min.js", + "node_modules/prismjs/components/prism-bash.min.js", + "node_modules/prismjs/components/prism-c.min.js", + "node_modules/prismjs/components/prism-cpp.min.js", + "node_modules/prismjs/components/prism-diff.min.js", + "node_modules/prismjs/components/prism-javascript.min.js", + "node_modules/prismjs/components/prism-latex.min.js", + "node_modules/prismjs/components/prism-markup.min.js", + "node_modules/prismjs/components/prism-markdown.min.js", + "node_modules/prismjs/components/prism-python.min.js", + "node_modules/prismjs/components/prism-typescript.min.js", + "node_modules/emoji-toolkit/lib/js/joypixels.min.js", + "node_modules/katex/dist/katex.min.js", + "node_modules/katex/dist/contrib/auto-render.min.js", + "node_modules/mermaid/dist/mermaid.min.js", + "node_modules/clipboard/dist/clipboard.min.js" + ], + "karmaConfig": "karma.conf.js" + } } - }, - "cli": { - "analytics": false + } } -} + }, + "cli": { + "analytics": false + } +} \ No newline at end of file diff --git a/src/app/app.module.ts b/src/app/app.module.ts index e6b0685..ab240a7 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,5 +1,5 @@ // Modules -import { NgModule, SecurityContext } from '@angular/core'; +import { APP_INITIALIZER, NgModule, SecurityContext, inject } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { HttpClientModule, HTTP_INTERCEPTORS, HttpClient } from '@angular/common/http'; @@ -36,6 +36,9 @@ import { WorkspaceService } from './services/workspace.service'; import { ApiService } from './services/api.service'; import { TokenInterceptorService } from './services/token-interceptor.service'; import { SnackBarService } from './services/snackbar.service'; +import { SettingsService } from './services/settings.service'; +import { of, tap } from 'rxjs'; +import { environment } from 'src/environments/environment'; @NgModule({ @@ -69,6 +72,38 @@ import { SnackBarService } from './services/snackbar.service'; PollEditorModule ], providers: [ + { + provide: APP_INITIALIZER, + useFactory: () => { + const settingsService = inject(SettingsService); + const httpClient = inject(HttpClient); + return () => new Promise((resolve, reject) => { + if (environment.production) { + // Load settings for production + httpClient.get('./config.json').pipe( + tap({ + next: (settings: any) => { + console.log(settings); + settingsService.apiUrl = settings.API_URL; + resolve(true); + }, + error: (err) => { + settingsService.apiUrl = "http://localhost:8000/api"; + resolve(true); + return of(null); + } + }) + ).subscribe(); + } else { + // Load settings for development + const settings = require('../config.json'); + settingsService.apiUrl = settings.api_url; + resolve(true); + } + }); + }, + multi: true, + }, HttpClient, ApiService, AuthService, diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index 8963bfc..e824228 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -11,13 +11,22 @@ import { GroupListModel, GroupModel } from '../models/group.model'; import { AccountListModel } from '../models/account.model'; import { Permissions, PolicyListModel, PolicyModel } from '../models/policy.model'; import { NewPollRequestBody, PollModel, PollListModel } from '../models/poll.model'; +import { SettingsService } from './settings.service'; -// @ts-ignore -const API_URL = environment.apiUrl; @Injectable({ providedIn: 'root' }) export class ApiService { - constructor(private http: HttpClient) {} + + API_URL: string | undefined; + + constructor(private http: HttpClient, private settings: SettingsService) { + this.API_URL = settings.apiUrl; + + if (this.API_URL === undefined) { + // @ts-ignore + this.API_URL = environment.apiUrl; + } + } // Authentification @@ -25,15 +34,15 @@ export class ApiService { let fd = new FormData(); fd.append('username', username); fd.append('password', password); - return this.http.post(API_URL + '/auth/jwt/login', fd, { observe: 'response' }); + return this.http.post(this.API_URL + '/auth/jwt/login', fd, { observe: 'response' }); } // logout() { - // return this.http.post(API_URL + '/auth/jwt/logout', {}); + // return this.http.post(this.API_URL + '/auth/jwt/logout', {}); // } register(fd: FormGroup): Observable { - return this.http.post(API_URL + '/auth/register', { + return this.http.post(this.API_URL + '/auth/register', { email: fd.get('email')?.value, password: fd.get('password')?.value, first_name: fd.get('first_name')?.value, @@ -45,7 +54,7 @@ export class ApiService { // Get list of workspaces getUserWorkspaces(): Observable { - return this.http.get(API_URL + '/workspaces'); + return this.http.get(this.API_URL + '/workspaces'); } // Get workspace by workspace_id @@ -64,69 +73,69 @@ export class ApiService { params = polls ? params.append("include", "polls") : params; } - return this.http.get(API_URL + '/workspaces/' + workspace_id, { params: params }); + return this.http.get(this.API_URL + '/workspaces/' + workspace_id, { params: params }); } // Create workspace createWorkspace(data: any) { - return this.http.post(API_URL + '/workspaces', data); + return this.http.post(this.API_URL + '/workspaces', data); } // Update workspace updateWorkspace(workspace_id: string, data: any) { - return this.http.patch(API_URL + '/workspaces/' + workspace_id, data); + return this.http.patch(this.API_URL + '/workspaces/' + workspace_id, data); } // Delete workspace deleteWorkspace(workspace_id: string) { - return this.http.delete(API_URL + '/workspaces/' + workspace_id); + return this.http.delete(this.API_URL + '/workspaces/' + workspace_id); } // Get list of members in workspace getWorkspaceMembers(workspace_id: string): Observable { - return this.http.get(API_URL + '/workspaces/' + workspace_id + '/members'); + return this.http.get(this.API_URL + '/workspaces/' + workspace_id + '/members'); } // Add member to workspace addMemberToWorkspace(workspace_id: string, data: any) { - return this.http.post(API_URL + '/workspaces/' + workspace_id + '/members', data); + return this.http.post(this.API_URL + '/workspaces/' + workspace_id + '/members', data); } // Remove member from workspace removeMemberFromWorkspace(workspace_id: string, member_workspace_id: string) { - return this.http.delete(API_URL + '/workspaces/' + workspace_id + '/members/' + member_workspace_id); + return this.http.delete(this.API_URL + '/workspaces/' + workspace_id + '/members/' + member_workspace_id); } // Get all policies getAllWorkspacesPolicies(workspace_id: string): Observable { - return this.http.get(API_URL + '/workspaces/' + workspace_id + '/policies'); + return this.http.get(this.API_URL + '/workspaces/' + workspace_id + '/policies'); } // Get workspace policy for specific account, or current user if account_id was not provided getWorkspacePolicy(workspace_id: string, account_id?: string): Observable { const options = account_id ? { params: { account_id: account_id } } : {}; - return this.http.get(API_URL + '/workspaces/' + workspace_id + '/policy', options); + return this.http.get(this.API_URL + '/workspaces/' + workspace_id + '/policy', options); } // Update workspace policy setWorkspacePolicy(workspace_id: string, data: any) { - return this.http.put(API_URL + '/workspaces/' + workspace_id + '/policy', data); + return this.http.put(this.API_URL + '/workspaces/' + workspace_id + '/policy', data); } getWorkspacePermissions(): Observable { - return this.http.get(API_URL + '/workspaces/permissions'); + return this.http.get(this.API_URL + '/workspaces/permissions'); } // Get list of groups in workspace getWorkspaceGroups(id: string): Observable { - return this.http.get(API_URL + '/workspaces/' + id + '/groups'); + return this.http.get(this.API_URL + '/workspaces/' + id + '/groups'); } // Groups // Create group in workspace createGroup(workspace_id: string, data: any) { - return this.http.post(API_URL + '/workspaces/' + workspace_id + '/groups', data); + return this.http.post(this.API_URL + '/workspaces/' + workspace_id + '/groups', data); } // Get group by id @@ -143,56 +152,56 @@ export class ApiService { params = members ? params.append("include", "members") : params; } - return this.http.get(API_URL + '/groups/' + group_id, { params: params }); + return this.http.get(this.API_URL + '/groups/' + group_id, { params: params }); } // Delete group by id deleteGroup(group_id: string) { - return this.http.delete(API_URL + '/groups/' + group_id); + return this.http.delete(this.API_URL + '/groups/' + group_id); } // Update group updateGroup(group_id: string, data: any) { - return this.http.patch(API_URL + '/groups/' + group_id, data); + return this.http.patch(this.API_URL + '/groups/' + group_id, data); } // Get list of members in group getGroupMembers(group_id: string): Observable { - return this.http.get(API_URL + '/groups/' + group_id + '/members'); + return this.http.get(this.API_URL + '/groups/' + group_id + '/members'); } // Add member to group addMemberToGroup(group_id: string, data: any) { - return this.http.post(API_URL + '/groups/' + group_id + '/members', data); + return this.http.post(this.API_URL + '/groups/' + group_id + '/members', data); } // Remove member from group removeMemberFromGroup(group_id: string, member_id: string) { - return this.http.delete(API_URL + '/groups/' + group_id + '/members/' + member_id); + return this.http.delete(this.API_URL + '/groups/' + group_id + '/members/' + member_id); } // Get all group policies getAllGroupsPolicies(group_id: string): Observable { - return this.http.get(API_URL + '/groups/' + group_id + '/policies'); + return this.http.get(this.API_URL + '/groups/' + group_id + '/policies'); } // Get group policy for specific account, or current user if account_id was not provided getGroupPolicy(group_id: string, account_id?: string): Observable { const options = account_id ? { params: { account_id: account_id } } : {}; - return this.http.get(API_URL + '/groups/' + group_id + '/policy', options); + return this.http.get(this.API_URL + '/groups/' + group_id + '/policy', options); } setGroupPolicy(group_id: string, data: any) { - return this.http.put(API_URL + '/groups/' + group_id + '/policy', data); + return this.http.put(this.API_URL + '/groups/' + group_id + '/policy', data); } getGroupPermissions(): Observable { - return this.http.get(API_URL + '/groups/permissions'); + return this.http.get(this.API_URL + '/groups/permissions'); } // Accounts getAllAccounts(): Observable { - return this.http.get(API_URL + '/accounts'); + return this.http.get(this.API_URL + '/accounts'); } @@ -200,12 +209,12 @@ export class ApiService { // Get list of polls in workspace getAllPolls(workspace_id: string): Observable { - return this.http.get(API_URL + '/workspaces/' + workspace_id + '/polls'); + return this.http.get(this.API_URL + '/workspaces/' + workspace_id + '/polls'); } // Create poll in workspace createPoll(workspace_id: string, data: NewPollRequestBody): Observable { - return this.http.post(API_URL + '/workspaces/' + workspace_id + '/polls', data); + return this.http.post(this.API_URL + '/workspaces/' + workspace_id + '/polls', data); } // Get poll by id @@ -221,7 +230,7 @@ export class ApiService { params = questions ? params.append("include", "members") : params; params = policies ? params.append("include", "policies") : params; } - return this.http.get(API_URL + '/polls/' + poll_id, { params: params }); + return this.http.get(this.API_URL + '/polls/' + poll_id, { params: params }); } } diff --git a/src/app/services/settings.service.spec.ts b/src/app/services/settings.service.spec.ts new file mode 100644 index 0000000..359cb6b --- /dev/null +++ b/src/app/services/settings.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { SettingsService } from './settings.service'; + +describe('SettingsService', () => { + let service: SettingsService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(SettingsService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/services/settings.service.ts b/src/app/services/settings.service.ts new file mode 100644 index 0000000..b1a7f75 --- /dev/null +++ b/src/app/services/settings.service.ts @@ -0,0 +1,8 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class SettingsService { + apiUrl: string | undefined; +} diff --git a/src/config.json b/src/config.json new file mode 100644 index 0000000..7f11054 --- /dev/null +++ b/src/config.json @@ -0,0 +1,3 @@ +{ + "api_url": "http://localhost:8000" +} \ No newline at end of file