1 import { Injectable, OnInit } from '@angular/core';
2 import { Http, Headers, RequestOptionsArgs, Response } from '@angular/http';
3 import { Location } from '@angular/common';
4 import { CookieService } from 'ngx-cookie';
5 import { Observable } from 'rxjs/Observable';
6 import { Subscriber } from 'rxjs/Subscriber';
7 import { BehaviorSubject } from 'rxjs/BehaviorSubject';
9 // Import RxJs required methods
10 import 'rxjs/add/operator/map';
11 import 'rxjs/add/operator/catch';
12 import 'rxjs/add/observable/throw';
13 import 'rxjs/add/operator/mergeMap';
16 import { XDSServerService, IXDSConfigProject } from "../services/xdsserver.service";
17 import { XDSAgentService } from "../services/xdsagent.service";
18 import { SyncthingService, ISyncThingProject, ISyncThingStatus } from "../services/syncthing.service";
19 import { AlertService, IAlert } from "../services/alert.service";
20 import { UtilsService } from "../services/utils.service";
22 export enum ProjectType {
27 export interface INativeProject {
31 export interface IProject {
36 remotePrjDef?: INativeProject | ISyncThingProject;
40 defaultSdkID?: string;
43 export interface IXDSAgentConfig {
48 export interface ILocalSTConfig {
55 export interface IxdsAgentPackage {
62 export interface IConfig {
64 xdsAgent: IXDSAgentConfig;
65 xdsAgentPackages: IxdsAgentPackage[];
66 projectsRootDir: string;
68 localSThg: ILocalSTConfig;
72 export class ConfigService {
74 public conf: Observable<IConfig>;
76 private confSubject: BehaviorSubject<IConfig>;
77 private confStore: IConfig;
78 private AgentConnectObs = null;
79 private stConnectObs = null;
81 constructor(private _window: Window,
82 private cookie: CookieService,
83 private xdsServerSvr: XDSServerService,
84 private xdsAgentSvr: XDSAgentService,
85 private stSvr: SyncthingService,
86 private alert: AlertService,
87 private utils: UtilsService,
90 this.confSubject = <BehaviorSubject<IConfig>>new BehaviorSubject(this.confStore);
91 this.conf = this.confSubject.asObservable();
93 // force to load projects
99 // Try to retrieve previous config from cookie
100 let cookConf = this.cookie.getObject("xds-config");
101 if (cookConf != null) {
102 this.confStore = <IConfig>cookConf;
104 // Set default config
106 xdsServerURL: this._window.location.origin + '/api/v1',
108 URL: 'http://localhost:8000',
111 xdsAgentPackages: [],
116 URL: "http://localhost:8384",
117 retry: 10, // 10 seconds
123 // Update XDS Agent tarball url
124 this.xdsServerSvr.getXdsAgentInfo().subscribe(nfo => {
125 this.confStore.xdsAgentPackages = [];
126 nfo.tarballs && nfo.tarballs.forEach(el =>
127 this.confStore.xdsAgentPackages.push({
134 this.confSubject.next(Object.assign({}, this.confStore));
138 // Save config into cookie
140 // Notify subscribers
141 this.confSubject.next(Object.assign({}, this.confStore));
143 // Don't save projects in cookies (too big!)
144 let cfg = Object.assign({}, this.confStore);
145 delete (cfg.projects);
146 this.cookie.putObject("xds-config", cfg);
150 // Setup connection with local XDS agent
151 if (this.AgentConnectObs) {
153 this.AgentConnectObs.unsubscribe();
155 this.AgentConnectObs = null;
158 let cfg = this.confStore.xdsAgent;
159 this.AgentConnectObs = this.xdsAgentSvr.connect(cfg.retry, cfg.URL)
160 .subscribe((sts) => {
161 //console.log("Agent sts", sts);
162 // FIXME: load projects from local XDS Agent and
163 // not directly from local syncthing
164 this._loadProjectFromLocalST();
167 if (error.indexOf("XDS local Agent not responding") !== -1) {
168 let msg = "<span><strong>" + error + "<br></strong>";
169 msg += "You may need to download and execute XDS-Agent.<br>";
171 let os = this.utils.getOSName(true);
172 let zurl = this.confStore.xdsAgentPackages && this.confStore.xdsAgentPackages.filter(elem => elem.os === os);
173 if (zurl && zurl.length) {
174 msg += " Download XDS-Agent tarball for " + zurl[0].os + " host OS ";
175 msg += "<a class=\"fa fa-download\" href=\"" + zurl[0].url + "\" target=\"_blank\"></a>";
178 this.alert.error(msg);
180 this.alert.error(error);
185 private _loadProjectFromLocalST() {
186 // Remove previous subscriber if existing
187 if (this.stConnectObs) {
189 this.stConnectObs.unsubscribe();
191 this.stConnectObs = null;
194 // FIXME: move this code and all logic about syncthing inside XDS Agent
195 // Setup connection with local SyncThing
196 let retry = this.confStore.localSThg.retry;
197 let url = this.confStore.localSThg.URL;
198 this.stConnectObs = this.stSvr.connect(retry, url).subscribe((sts) => {
199 this.confStore.localSThg.ID = sts.ID;
200 this.confStore.localSThg.tilde = sts.tilde;
201 if (this.confStore.projectsRootDir === "") {
202 this.confStore.projectsRootDir = sts.tilde;
205 // Rebuild projects definition from local and remote syncthing
206 this.confStore.projects = [];
208 this.xdsServerSvr.getProjects().subscribe(remotePrj => {
209 this.stSvr.getProjects().subscribe(localPrj => {
210 remotePrj.forEach(rPrj => {
211 let lPrj = localPrj.filter(item => item.id === rPrj.id);
212 if (lPrj.length > 0) {
217 type: ProjectType.SYNCTHING, // FIXME support other types
218 remotePrjDef: Object.assign({}, rPrj),
219 localPrjDef: Object.assign({}, lPrj[0]),
221 this.confStore.projects.push(pp);
224 this.confSubject.next(Object.assign({}, this.confStore));
225 }), error => this.alert.error('Could not load initial state of local projects.');
226 }), error => this.alert.error('Could not load initial state of remote projects.');
229 if (error.indexOf("Syncthing local daemon not responding") !== -1) {
230 let msg = "<span><strong>" + error + "<br></strong>";
231 msg += "Please check that local XDS-Agent is running.<br>";
233 this.alert.error(msg);
235 this.alert.error(error);
240 set syncToolURL(url: string) {
241 this.confStore.localSThg.URL = url;
245 set xdsAgentRetry(r: number) {
246 this.confStore.localSThg.retry = r;
247 this.confStore.xdsAgent.retry = r;
251 set xdsAgentUrl(url: string) {
252 this.confStore.xdsAgent.URL = url;
257 set projectsRootDir(p: string) {
258 if (p.charAt(0) === '~') {
259 p = this.confStore.localSThg.tilde + p.substring(1);
261 this.confStore.projectsRootDir = p;
265 getLabelRootName(): string {
266 let id = this.confStore.localSThg.ID;
267 if (!id || id === "") {
270 return id.slice(0, 15);
273 addProject(prj: IProject) {
274 // Substitute tilde with to user home path
275 prj.path = prj.path.trim();
276 if (prj.path.charAt(0) === '~') {
277 prj.path = this.confStore.localSThg.tilde + prj.path.substring(1);
279 // Must be a full path (on Linux or Windows)
280 } else if (!((prj.path.charAt(0) === '/') ||
281 (prj.path.charAt(1) === ':' && (prj.path.charAt(2) === '\\' || prj.path.charAt(2) === '/')))) {
282 prj.path = this.confStore.projectsRootDir + '/' + prj.path;
285 if (prj.id == null) {
286 // FIXME - must be done on server side
287 let prefix = this.getLabelRootName() || new Date().toISOString();
288 let splath = prj.path.split('/');
289 prj.id = prefix + "_" + splath[splath.length - 1];
292 if (this._getProjectIdx(prj.id) !== -1) {
293 this.alert.warning("Project already exist (id=" + prj.id + ")", true);
297 // TODO - support others project types
298 if (prj.type !== ProjectType.SYNCTHING) {
299 this.alert.error('Project type not supported yet (type: ' + prj.type + ')');
303 let sdkPrj: IXDSConfigProject = {
307 hostSyncThingID: this.confStore.localSThg.ID,
308 defaultSdkID: prj.defaultSdkID,
311 // Send config to XDS server
313 this.xdsServerSvr.addProject(sdkPrj)
314 .subscribe(resStRemotePrj => {
315 newPrj.remotePrjDef = resStRemotePrj;
317 // FIXME REWORK local ST config
318 // move logic to server side tunneling-back by WS
320 // Now setup local config
321 let stLocPrj: ISyncThingProject = {
325 remoteSyncThingID: resStRemotePrj.builderSThgID
328 // Set local Syncthing config
329 this.stSvr.addProject(stLocPrj)
330 .subscribe(resStLocalPrj => {
331 newPrj.localPrjDef = resStLocalPrj;
333 // FIXME: maybe reduce subject to only .project
334 //this.confSubject.next(Object.assign({}, this.confStore).project);
335 this.confStore.projects.push(Object.assign({}, newPrj));
336 this.confSubject.next(Object.assign({}, this.confStore));
339 this.alert.error("Configuration local ERROR: " + err);
343 this.alert.error("Configuration remote ERROR: " + err);
347 deleteProject(prj: IProject) {
348 let idx = this._getProjectIdx(prj.id);
350 throw new Error("Invalid project id (id=" + prj.id + ")");
352 this.xdsServerSvr.deleteProject(prj.id)
354 this.stSvr.deleteProject(prj.id)
356 this.confStore.projects.splice(idx, 1);
358 this.alert.error("Delete local ERROR: " + err);
361 this.alert.error("Delete remote ERROR: " + err);
365 private _getProjectIdx(id: string): number {
366 return this.confStore.projects.findIndex((item) => item.id === id);