• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bpatrik / pigallery2 / 16674651331

01 Aug 2025 12:08PM UTC coverage: 64.145% (-0.1%) from 64.267%
16674651331

Pull #1010

github

web-flow
Merge 3807a7bdc into 5f6389415
Pull Request #1010: Upgrade packages, node and alpine

1135 of 2020 branches covered (56.19%)

Branch coverage included in aggregate %.

8 of 11 new or added lines in 4 files covered. (72.73%)

4 existing lines in 4 files now uncovered.

4318 of 6481 relevant lines covered (66.63%)

13321.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

58.72
/src/backend/server.ts
1
import {Config} from '../common/config/private/Config';
1✔
2
import * as express from 'express';
1✔
3
import * as cookieParser from 'cookie-parser';
1✔
4
import * as _http from 'http';
1✔
5
import {Server as HttpServer} from 'http';
6
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
7
// @ts-ignore
8
import * as locale from 'locale';
1✔
9
import {ObjectManagers} from './model/ObjectManagers';
1✔
10
import {Logger} from './Logger';
1✔
11
import {LoggerRouter} from './routes/LoggerRouter';
1✔
12
import {ConfigDiagnostics} from './model/diagnostics/ConfigDiagnostics';
1✔
13
import {Localizations} from './model/Localizations';
1✔
14
import {CookieNames} from '../common/CookieNames';
1✔
15
import {Router} from './routes/Router';
1✔
16
import {PhotoProcessing} from './model/fileaccess/fileprocessing/PhotoProcessing';
1✔
17
import {Event} from '../common/event/Event';
1✔
18
import {QueryParams} from '../common/QueryParams';
1✔
19
import {ConfigClassBuilder} from 'typeconfig/node';
1✔
20
import {ConfigClassOptions} from 'typeconfig/src/decorators/class/IConfigClass';
21
import {ServerConfig} from '../common/config/private/PrivateConfig';
22

23
// eslint-disable-next-line @typescript-eslint/no-require-imports
24
const session = require('cookie-session');
1✔
25

26
declare const process: NodeJS.Process;
27

28
const LOG_TAG = '[server]';
1✔
29

30
export class Server {
1✔
31
  public onStarted = new Event<void>();
23✔
32
  public app: express.Express;
33
  private server: HttpServer;
34

35
  public static instance: Server = null;
1✔
36

37
  public static getInstance(): Server {
38
    if (!this.instance) {
×
39
      this.instance = new Server();
×
40
    }
41
    return this.instance;
×
42
  }
43

44
  constructor(listen = true) {
×
45
    if (!(process.env.NODE_ENV === 'production')) {
23✔
46
      Logger.info(
23✔
47
        LOG_TAG,
48
        'Running in DEBUG mode, set env variable NODE_ENV=production to disable '
49
      );
50
    }
51
    this.init(listen).catch(console.error);
23✔
52
  }
53

54
  get Server(): HttpServer {
55
    return this.server;
27✔
56
  }
57

58
  async init(listen = true): Promise<void> {
×
59
    this.app = express();
23✔
60
    LoggerRouter.route(this.app);
23✔
61
    this.app.set('view engine', 'ejs');
23✔
62

63
    Logger.info(LOG_TAG, 'running diagnostics...');
23✔
64
    await ConfigDiagnostics.runDiagnostics();
23✔
65
    Logger.verbose(
23✔
66
      LOG_TAG,
67
      () => 'using config from ' +
×
68
        (
69
          ConfigClassBuilder.attachPrivateInterface(Config)
70
            .__options as ConfigClassOptions<ServerConfig>
71
        ).configPath +
72
        ':'
73
    );
74
    Logger.verbose(LOG_TAG, () => JSON.stringify(Config.toJSON({attachDescription: false}), (k, v) => {
23✔
75
      const MAX_LENGTH = 80;
×
76
      if (typeof v === 'string' && v.length > MAX_LENGTH) {
×
77
        v = v.slice(0, MAX_LENGTH - 3) + '...';
×
78
      }
79
      return v;
×
80
    }, 2));
81

82

83
    /**
84
     * Session above all
85
     */
86

87
    this.app.use(
23✔
88
      session({
89
        name: CookieNames.session,
90
        keys: Config.Server.sessionSecret,
91
      })
92
    );
93

94
    /**
95
     * Parse parameters in POST
96
     */
97
    // for parsing application/json
98
    this.app.use(express.json());
23✔
99
    this.app.use(cookieParser());
23✔
100

101
    // enable token generation but do not check it
102
    this.app.post(
23✔
103
      [Config.Server.apiPath + '/user/login', Config.Server.apiPath + '/share/login'],
104
    );
105
    this.app.get(
23✔
106
      [Config.Server.apiPath + '/user/me', Config.Server.apiPath + '/share/:' + QueryParams.gallery.sharingKey_params],
107
    );
108

109
    PhotoProcessing.init();
23✔
110
    Localizations.init();
23✔
111

112
    this.app.use(locale(Config.Server.languages, 'en'));
23✔
113
    await ObjectManagers.getInstance().init();
23✔
114

115
    Router.route(this.app);
23✔
116

117
    // Get PORT from environment and store in Express.
118
    this.app.set('port', Config.Server.port);
23✔
119

120
    // Create HTTP server.
121
    this.server = _http.createServer(this.app);
23✔
122

123
    // Listen on provided PORT, on all network interfaces.
124
    if (listen) {
23!
125
      this.server.listen(Config.Server.port, Config.Server.host);
×
126
    }
127
    this.server.on('error', this.onError);
23✔
128
    this.server.on('listening', this.onListening);
23✔
129
    this.server.on('close', this.onClose);
23✔
130

131
    // Sigterm handler
132
    process.removeAllListeners('SIGTERM');
23✔
133
    process.on('SIGTERM', this.SIGTERM);
23✔
134

135
    if (!listen) {
23✔
136
      this.onStarted.trigger();
23✔
137
    }
138
  }
139

140
  private SIGTERM = () => {
23✔
141
    Logger.info(LOG_TAG, 'SIGTERM signal received');
×
142
    this.server.close(() => {
×
143
      process.exit(0);
×
144
    });
145
  };
146

147
  /**
148
   *
149
   * Event listener for HTTP server "error" event.
150
   */
151
  private onError = (error: {
23✔
152
    errno?: number,
153
    code?: string,
154
    path?: string,
155
    syscall?: string,
156
    stack?: string
157
  }) => {
158
    if (error.syscall !== 'listen') {
×
159
      Logger.error(LOG_TAG, 'Server error', error);
×
160
      throw error;
×
161
    }
162

163
    const bind = Config.Server.host + ':' + Config.Server.port;
×
164

165
    // handle specific listen error with friendly messages
166
    switch (error.code) {
×
167
      case 'EACCES':
168
        Logger.error(LOG_TAG, bind + ' requires elevated privileges');
×
169
        process.exit(1);
×
170
        break;
×
171
      case 'EADDRINUSE':
172
        Logger.error(LOG_TAG, bind + ' is already in use');
×
173
        process.exit(1);
×
174
        break;
×
175
      default:
176
        throw error;
×
177
    }
178
  };
179

180
  /**
181
   * Event listener for HTTP server "listening" event.
182
   */
183
  private onListening = () => {
23✔
184
    const addr = this.server.address();
27✔
185
    const bind =
186
      typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
27!
187
    Logger.info(LOG_TAG, 'Listening on ' + bind);
27✔
188
    this.onStarted.trigger();
27✔
189
  };
190

191
  /**
192
   * Event listener for HTTP server "close" event.
193
   */
194
  private onClose = () => {
23✔
195
    Logger.info(LOG_TAG, 'Closed http server');
27✔
196
  };
197

198
  public Stop(): Promise<void> {
199
    return new Promise((resolve, reject) => {
×
NEW
200
      if (!this.server.listening) {
×
201
        return resolve();
×
202
      }
203
      this.server.close((err) => {
×
204
        if (!err) {
×
205
          return resolve();
×
206
        }
207
        reject(err);
×
208
      });
209
    });
210
  }
211
}
212

213

214

215

216

217

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc