beyondcode / herd-community

89 stars 1 forks source link

[Bug]: License key removed when starting Herd without internet connection #558

Closed fstanic closed 6 months ago

fstanic commented 7 months ago

Platform

Windows

Operating system version

Windows 10 Pro (22H2 19045.4170)

System architecture

Windows

Herd Version

1.0.1

PHP Version

No response

Bug description

When starting Laravel Heard if computer is offline (without internet connection) the license key is removed after few seconds, Pro features disappears and it switches to Laravel Basic.

Screenshot 2024-03-28 200721

When internet connection is restored you need to manually enter license key every time this happens to get the Pro features.

Screenshot 2024-03-28 200859

Steps to reproduce

Start Herd Pro without internet connection on computer.

Relevant log output

Log from main.log file:

[2024-03-28 20:43:46.560] [info]  Starting Herd...
[2024-03-28 20:43:46.596] [info]  Copying Nginx config...
[2024-03-28 20:43:46.599] [info]  Copying binaries to C:\Users\username\.config\herd\bin
[2024-03-28 20:43:46.730] [info]  [
  {
    updating: false,
    installed: false,
    version: '8.3',
    completeVersion: '',
    internalVersion: 2
  }
]
[2024-03-28 20:43:46.746] [info]  [nginx] Starting Nginx...
[2024-03-28 20:43:46.746] [info]  [nginx] Config: C:\Users\username\.config\herd\config\nginx\nginx.conf
[2024-03-28 20:43:46.751] [info]  log-pid.
[2024-03-28 20:43:46.752] [info]  Wrote PID (6056) to C:\Users\username\.config\herd\.pid\nginx.pid
[2024-03-28 20:43:46.791] [info]  Notifying windows of service update: Dumps - active
[2024-03-28 20:43:46.797] [info]  Starting CGI
[2024-03-28 20:43:46.797] [info]  [PHP] Starting PHP 83 on port 9083
[2024-03-28 20:43:46.797] [info]  [PHP] Path: C:\Users\username\.config\herd\bin\php83\php-cgi.exe
[2024-03-28 20:43:46.801] [info]  log-pid.
[2024-03-28 20:43:46.802] [info]  Wrote PID (10228) to C:\Users\username\.config\herd\.pid\php-8_3.pid
[2024-03-28 20:43:46.811] [info]  Starting CGI
[2024-03-28 20:43:46.811] [info]  [PHP-Debug] Starting PHP 83 on port 9183
[2024-03-28 20:43:46.812] [info]  [PHP-Debug] Path: C:\Users\username\.config\herd\bin\php83\php-cgi.exe
[2024-03-28 20:43:46.812] [info]  [PHP-Debug] INI Path: C:\Users\username\.config\herd\config\php\83
[2024-03-28 20:43:46.812] [info]  [PHP-Debug] INI File: C:\Users\username\.config\herd\config\php\83\debug\debug.ini
[2024-03-28 20:43:46.815] [info]  log-pid.
[2024-03-28 20:43:46.816] [info]  Wrote PID (1524) to C:\Users\username\.config\herd\.pid\php-8_3 (debug).pid
[2024-03-28 20:43:46.830] [info]  Starting MySQL (MySQL) on port 3306 with datapath 50b2cd14-0c8e-406f-8ab0-db81b539ce13
[2024-03-28 20:43:46.830] [info]  log-pid.50b2cd14-0c8e-406f-8ab0-db81b539ce13
[2024-03-28 20:43:46.841] [info]  log-pid.50b2cd14-0c8e-406f-8ab0-db81b539ce13
[2024-03-28 20:43:46.842] [info]  Wrote PID (7256) to C:\Users\username\.config\herd\.pid\50b2cd14-0c8e-406f-8ab0-db81b539ce13.pid
[2024-03-28 20:43:46.844] [info]  Checking for PHP updates [
  {
    updating: false,
    installed: false,
    version: '8.3',
    completeVersion: '',
    internalVersion: 2
  },
  {
    updating: false,
    installed: false,
    version: '8.2',
    completeVersion: '',
    internalVersion: 2
  },
  {
    updating: false,
    installed: false,
    version: '8.1',
    completeVersion: '',
    internalVersion: 2
  },
  {
    updating: false,
    installed: false,
    version: '8.0',
    completeVersion: '',
    internalVersion: 2
  },
  {
    updating: false,
    installed: false,
    version: '7.4',
    completeVersion: '',
    internalVersion: 2
  }
]
[2024-03-28 20:43:46.861] [info]  Starting API Server
[2024-03-28 20:43:47.161] [info]  Watching paths:  [ 'C:\\Users\\username\\Herd' ]
[2024-03-28 20:43:47.178] [info]  Started NGINX with PID 6056
[2024-03-28 20:43:47.179] [info]  Notifying windows of service update: NGINX - active
[2024-03-28 20:43:47.179] [info]  [Mails] started on port 2525
[2024-03-28 20:43:47.179] [info]  Notifying windows of service update: Mail - active
[2024-03-28 20:43:47.184] [info]  Started PHP-8.3 with PID 10228
[2024-03-28 20:43:47.185] [info]  Notifying windows of service update: PHP-8.3 - active
[2024-03-28 20:43:47.185] [info]  Started PHP-8.3 (Debug) with PID 1524
[2024-03-28 20:43:47.186] [info]  Notifying windows of service update: PHP-8.3 (Debug) - active
[2024-03-28 20:43:47.190] [info]  Started MySQL with PID 7256
[2024-03-28 20:43:47.191] [info]  Notifying windows of service update: MySQL - active
[2024-03-28 20:43:47.192] [info]  Internal API listening at http://localhost:9001
[2024-03-28 20:43:47.196] [info]  {
  message: 'getaddrinfo ENOTFOUND herdphp.com',
  name: 'Error',
  stack: 'Error: getaddrinfo ENOTFOUND herdphp.com\n' +
    '    at Function.from (C:\\Program Files\\Herd\\resources\\app.asar\\node_modules\\axios\\dist\\node\\axios.cjs:837:14)\n' +
    '    at RedirectableRequest.handleRequestError (C:\\Program Files\\Herd\\resources\\app.asar\\node_modules\\axios\\dist\\node\\axios.cjs:3083:25)\n' +
    '    at RedirectableRequest.emit (node:events:517:28)\n' +
    '    at ClientRequest.<anonymous> (C:\\Program Files\\Herd\\resources\\app.asar\\node_modules\\follow-redirects\\index.js:14:24)\n' +
    '    at ClientRequest.emit (node:events:517:28)\n' +
    '    at TLSSocket.socketErrorListener (node:_http_client:501:9)\n' +
    '    at TLSSocket.emit (node:events:517:28)\n' +
    '    at emitErrorNT (node:internal/streams/destroy:151:8)\n' +
    '    at emitErrorCloseNT (node:internal/streams/destroy:116:3)\n' +
    '    at process.processTicksAndRejections (node:internal/process/task_queues:82:21)',
  config: {
    transitional: {
      silentJSONParsing: true,
      forcedJSONParsing: true,
      clarifyTimeoutError: false
    },
    adapter: [ 'xhr', 'http' ],
    transformRequest: [
      '[function] function transformRequest(data, headers) {\n' +
        "    const contentType = headers.getContentType() || '';\n" +
        "    const hasJSONContentType = contentType.indexOf('application/json') > -1;\n" +
        '    const isObjectPayload = utils$1.isObject(data);\n' +
        '\n' +
        '    if (isObjectPayload && utils$1.isHTMLForm(data)) {\n' +
        '      data = new FormData(data);\n' +
        '    }\n' +
        '\n' +
        '    const isFormData = utils$1.isFormData(data);\n' +
        '\n' +
        '    if (isFormData) {\n' +
        '      if (!hasJSONContentType) {\n' +
        '        return data;\n' +
        '      }\n' +
        '      return hasJSONContentType ? JSON.stringify(formDataToJSON(data)) : data;\n' +
        '    }\n' +
        '\n' +
        '    if (utils$1.isArrayBuffer(data) ||\n' +
        '      utils$1.isBuffer(data) ||\n' +
        '      utils$1.isStream(data) ||\n' +
        '      utils$1.isFile(data) ||\n' +
        '      utils$1.isBlob(data)\n' +
        '    ) {\n' +
        '      return data;\n' +
        '    }\n' +
        '    if (utils$1.isArrayBufferView(data)) {\n' +
        '      return data.buffer;\n' +
        '    }\n' +
        '    if (utils$1.isURLSearchParams(data)) {\n' +
        "      headers.setContentType('application/x-www-form-urlencoded;charset=utf-8', false);\n" +
        '      return data.toString();\n' +
        '    }\n' +
        '\n' +
        '    let isFileList;\n' +
        '\n' +
        '    if (isObjectPayload) {\n' +
        "      if (contentType.indexOf('application/x-www-form-urlencoded') > -1) {\n" +
        '        return toURLEncodedForm(data, this.formSerializer).toString();\n' +
        '      }\n' +
        '\n' +
        "      if ((isFileList = utils$1.isFileList(data)) || contentType.indexOf('multipart/form-data') > -1) {\n" +
        '        const _FormData = this.env && this.env.FormData;\n' +
        '\n' +
        '        return toFormData(\n' +
        "          isFileList ? {'files[]': data} : data,\n" +
        '          _FormData && new _FormData(),\n' +
        '          this.formSerializer\n' +
        '        );\n' +
        '      }\n' +
        '    }\n' +
        '\n' +
        '    if (isObjectPayload || hasJSONContentType ) {\n' +
        "      headers.setContentType('application/json', false);\n" +
        '      return stringifySafely(data);\n' +
        '    }\n' +
        '\n' +
        '    return data;\n' +
        '  }'
    ],
    transformResponse: [
      '[function] function transformResponse(data) {\n' +
        '    const transitional = this.transitional || defaults.transitional;\n' +
        '    const forcedJSONParsing = transitional && transitional.forcedJSONParsing;\n' +
        "    const JSONRequested = this.responseType === 'json';\n" +
        '\n' +
        '    if (data && utils$1.isString(data) && ((forcedJSONParsing && !this.responseType) || JSONRequested)) {\n' +
        '      const silentJSONParsing = transitional && transitional.silentJSONParsing;\n' +
        '      const strictJSONParsing = !silentJSONParsing && JSONRequested;\n' +
        '\n' +
        '      try {\n' +
        '        return JSON.parse(data);\n' +
        '      } catch (e) {\n' +
        '        if (strictJSONParsing) {\n' +
        "          if (e.name === 'SyntaxError') {\n" +
        '            throw AxiosError.from(e, AxiosError.ERR_BAD_RESPONSE, this, null, this.response);\n' +
        '          }\n' +
        '          throw e;\n' +
        '        }\n' +
        '      }\n' +
        '    }\n' +
        '\n' +
        '    return data;\n' +
        '  }'
    ],
    timeout: 0,
    xsrfCookieName: 'XSRF-TOKEN',
    xsrfHeaderName: 'X-XSRF-TOKEN',
    maxContentLength: -1,
    maxBodyLength: -1,
    env: {
      FormData: '[function] function FormData(options) {\n' +
        '  if (!(this instanceof FormData)) {\n' +
        '    return new FormData(options);\n' +
        '  }\n' +
        '\n' +
        '  this._overheadLength = 0;\n' +
        '  this._valueLength = 0;\n' +
        '  this._valuesToMeasure = [];\n' +
        '\n' +
        '  CombinedStream.call(this);\n' +
        '\n' +
        '  options = options || {};\n' +
        '  for (var option in options) {\n' +
        '    this[option] = options[option];\n' +
        '  }\n' +
        '}',
      Blob: '[function] class Blob {\n' +
        '  /**\n' +
        '   * @typedef {string|ArrayBuffer|ArrayBufferView|Blob} SourcePart\n' +
        '   */\n' +
        '\n' +
        '  /**\n' +
        '   * @param {SourcePart[]} [sources]\n' +
        '   * @param {{\n' +
        '   *   endings? : string,\n' +
        '   *   type? : string,\n' +
        '   * }} [options]\n' +
        '   * @constructs {Blob}\n' +
        '   */\n' +
        '  constructor(sources = [], options) {\n' +
        '    if (sources === null ||\n' +
        "        typeof sources[SymbolIterator] !== 'function' ||\n" +
        "        typeof sources === 'string') {\n" +
        "      throw new ERR_INVALID_ARG_TYPE('sources', 'a sequence', sources);\n" +
        '    }\n' +
        "    validateDictionary(options, 'options');\n" +
        '    let {\n' +
        "      type = '',\n" +
        "      endings = 'transparent',\n" +
        '    } = options ?? kEmptyObject;\n' +
        '\n' +
        '    endings = `${endings}`;\n' +
        "    if (endings !== 'transparent' && endings !== 'native')\n" +
        "      throw new ERR_INVALID_ARG_VALUE('options.endings', endings);\n" +
        '\n' +
        '    let length = 0;\n' +
        '    const sources_ = ArrayFrom(sources, (source) => {\n' +
        '      const { 0: len, 1: src } = getSource(source, endings);\n' +
        '      length += len;\n' +
        '      return src;\n' +
        '    });\n' +
        '\n' +
        '    if (length > kMaxLength)\n' +
        '      throw new ERR_BUFFER_TOO_LARGE(kMaxLength);\n' +
        '\n' +
        '    this[kHandle] = _createBlob(sources_, length);\n' +
        '    this[kLength] = length;\n' +
        '\n' +
        '    type = `${type}`;\n' +
        '    this[kType] = RegExpPrototypeExec(disallowedTypeCharacters, type) !== null ?\n' +
        "      '' : StringPrototypeToLowerCase(type);\n" +
        '\n' +
        '    // eslint-disable-next-line no-constructor-return\n' +
        '    return makeTransferable(this);\n' +
        '  }\n' +
        '\n' +
        '  [kInspect](depth, options) {\n' +
        '    if (depth < 0)\n' +
        '      return this;\n' +
        '\n' +
        '    const opts = {\n' +
        '      ...options,\n' +
        '      depth: options.depth == null ? null : options.depth - 1,\n' +
        '    };\n' +
        '\n' +
        '    return `Blob ${inspect({\n' +
        '      size: this.size,\n' +
        '      type: this.type,\n' +
        '    }, opts)}`;\n' +
        '  }\n' +
        '\n' +
        '  [kClone]() {\n' +
        '    const handle = this[kHandle];\n' +
        '    const type = this[kType];\n' +
        '    const length = this[kLength];\n' +
        '    return {\n' +
        '      data: { handle, type, length },\n' +
        "      deserializeInfo: 'internal/blob:ClonedBlob',\n" +
        '    };\n' +
        '  }\n' +
        '\n' +
        '  [kDeserialize]({ handle, type, length }) {\n' +
        '    this[kHandle] = handle;\n' +
        '    this[kType] = type;\n' +
        '    this[kLength] = length;\n' +
        '  }\n' +
        '\n' +
        '  /**\n' +
        '   * @readonly\n' +
        '   * @type {string}\n' +
        '   */\n' +
        '  get type() {\n' +
        '    if (!isBlob(this))\n' +
        "      throw new ERR_INVALID_THIS('Blob');\n" +
        '    return this[kType];\n' +
        '  }\n' +
        '\n' +
        '  /**\n' +
        '   * @readonly\n' +
        '   * @type {number}\n' +
        '   */\n' +
        '  get size() {\n' +
        '    if (!isBlob(this))\n' +
        "      throw new ERR_INVALID_THIS('Blob');\n" +
        '    return this[kLength];\n' +
        '  }\n' +
        '\n' +
        '  /**\n' +
        '   * @param {number} [start]\n' +
        '   * @param {number} [end]\n' +
        '   * @param {string} [contentType]\n' +
        '   * @returns {Blob}\n' +
        '   */\n' +
        "  slice(start = 0, end = this[kLength], contentType = '') {\n" +
        '    if (!isBlob(this))\n' +
        "      throw new ERR_INVALID_THIS('Blob');\n" +
        '    if (start < 0) {\n' +
        '      start = MathMax(this[kLength] + start, 0);\n' +
        '    } else {\n' +
        '      start = MathMin(start, this[kLength]);\n' +
        '    }\n' +
        '    start |= 0;\n' +
        '\n' +
        '    if (end < 0) {\n' +
        '      end = MathMax(this[kLength] + end, 0);\n' +
        '    } else {\n' +
        '      end = MathMin(end, this[kLength]);\n' +
        '    }\n' +
        '    end |= 0;\n' +
        '\n' +
        '    contentType = `${contentType}`;\n' +
        '    if (RegExpPrototypeExec(disallowedTypeCharacters, contentType) !== null) {\n' +
        "      contentType = '';\n" +
        '    } else {\n' +
        '      contentType = StringPrototypeToLowerCase(contentType);\n' +
        '    }\n' +
        '\n' +
        '    const span = MathMax(end - start, 0);\n' +
        '\n' +
        '    return createBlob(\n' +
        '      this[kHandle].slice(start, start + span),\n' +
        '      span,\n' +
        '      contentType);\n' +
        '  }\n' +
        '\n' +
        '  /**\n' +
        '   * @returns {Promise<ArrayBuffer>}\n' +
        '   */\n' +
        '  arrayBuffer() {\n' +
        '    if (!isBlob(this))\n' +
        "      return PromiseReject(new ERR_INVALID_THIS('Blob'));\n" +
        '\n' +
        "    // If there's already a promise in flight for the content,\n" +
        "    // reuse it, but only while it's in flight. After the cached\n" +
        '    // promise resolves it will be cleared, allowing it to be\n' +
        '    // garbage collected as soon as possible.\n' +
        '    if (this[kArrayBufferPromise])\n' +
        '      return this[kArrayBufferPromise];\n' +
        '\n' +
        '    const job = new FixedSizeBlobCopyJob(this[kHandle]);\n' +
        '\n' +
        '    const ret = job.run();\n' +
        '\n' +
        '    // If the job returns a value immediately, the ArrayBuffer\n' +
        '    // was generated synchronously and should just be returned\n' +
        '    // directly.\n' +
        '    if (ret !== undefined)\n' +
        '      return PromiseResolve(ret);\n' +
        '\n' +
        '    const {\n' +
        '      promise,\n' +
        '      resolve,\n' +
        '      reject,\n' +
        '    } = createDeferredPromise();\n' +
        '\n' +
        '    job.ondone = (err, ab) => {\n' +
        '      if (err !== undefined)\n' +
        '        return reject(new AbortError(undefined, { cause: err }));\n' +
        '      resolve(ab);\n' +
        '    };\n' +
        '    this[kArrayBufferPromise] =\n' +
        '    SafePromisePrototypeFinally(\n' +
        '      promise,\n' +
        '      () => this[kArrayBufferPromise] = undefined);\n' +
        '\n' +
        '    return this[kArrayBufferPromise];\n' +
        '  }\n' +
        '\n' +
        '  /**\n' +
        '   * @returns {Promise<string>}\n' +
        '   */\n' +
        '  async text() {\n' +
        '    if (!isBlob(this))\n' +
        "      throw new ERR_INVALID_THIS('Blob');\n" +
        '\n' +
        '    dec ??= new TextDecoder();\n' +
        '\n' +
        '    return dec.decode(await this.arrayBuffer());\n' +
        '  }\n' +
        '\n' +
        '  /**\n' +
        '   * @returns {ReadableStream}\n' +
        '   */\n' +
        '  stream() {\n' +
        '    if (!isBlob(this))\n' +
        "      throw new ERR_INVALID_THIS('Blob');\n" +
        '\n' +
        '    const self = this;\n' +
        '    return new lazyReadableStream({\n' +
        '      async start() {\n' +
        '        this[kState] = await self.arrayBuffer();\n' +
        '        this[kIndex] = 0;\n' +
        '      },\n' +
        '\n' +
        '      pull(controller) {\n' +
        '        if (this[kState].byteLength - this[kIndex] <= kMaxChunkSize) {\n' +
        '          controller.enqueue(new Uint8Array(this[kState], this[kIndex]));\n' +
        '          controller.close();\n' +
        '          this[kState] = undefined;\n' +
        '        } else {\n' +
        '          controller.enqueue(new Uint8Array(this[kState], this[kIndex], kMaxChunkSize));\n' +
        '          this[kIndex] += kMaxChunkSize;\n' +
        '        }\n' +
        '      },\n' +
        '    });\n' +
        '  }\n' +
        '}'
    },
    validateStatus: '[function] function validateStatus(status) {\n' +
      '    return status >= 200 && status < 300;\n' +
      '  }',
    headers: {
      Accept: 'application/json, text/plain, */*',
      'User-Agent': 'axios/1.6.2',
      'Accept-Encoding': 'gzip, compress, deflate, br'
    },
    method: 'get',
    url: 'https://herdphp.com/api/windows/update?version=1.0.1'
  },
  code: 'ENOTFOUND',
  status: null
}
[2024-03-28 20:43:47.320] [info]  Notifying windows of service update: Dumps - inactive
[2024-03-28 20:43:47.329] [info]  Found service 50b2cd14-0c8e-406f-8ab0-db81b539ce13 - stopping and removing
[2024-03-28 20:43:47.330] [info]  Stopping MySQL with PID 7256
[2024-03-28 20:43:47.413] [info]  Stopped MySQL
[2024-03-28 20:43:47.418] [info]  [Mails] stopped inactive
[2024-03-28 20:43:47.419] [info]  Notifying windows of service update: Mail - inactive
[2024-03-28 20:43:47.433] [info]  MySQL exited
[2024-03-28 20:43:47.433] [info]  Deleted PID file C:\Users\username\.config\herd\.pid\50b2cd14-0c8e-406f-8ab0-db81b539ce13.pid
[2024-03-28 20:43:47.434] [info]  Notifying windows of service update: MySQL - inactive
[2024-03-28 20:43:47.435] [info]  MySQL exited with code 1 - PID: 7256
[2024-03-28 20:43:47.435] [info]  Notifying windows of service update: MySQL - inactive
[2024-03-28 20:43:47.443] [info]  log-pid.50b2cd14-0c8e-406f-8ab0-db81b539ce13
[2024-03-28 20:43:47.444] [info]  log-pid.50b2cd14-0c8e-406f-8ab0-db81b539ce13
[2024-03-28 20:43:47.446] [info]  Hosts watcher ready
[2024-03-28 20:43:47.447] [info]  Updating hosts file via HerdHelper Windows Service
[2024-03-28 20:43:47.461] [info]  Server says: Hosts file updated.
[2024-03-28 20:43:47.560] [info]  Removed service 50b2cd14-0c8e-406f-8ab0-db81b539ce13
mpociot commented 6 months ago

This is now fixed with Herd 1.1.0