OneBot: Leantime 改造版源码(BOM/Univer 表格/AI 接管/品牌替换等)

This commit is contained in:
wangruiguo
2026-09-03 18:49:20 +08:00
commit d647428529
3501 changed files with 1988906 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
<?php
namespace Leantime\Core\Configuration;
/**
* appSettings class - System appSettings
*/
class AppSettings
{
public string $appVersion = '3.9.8';
public string $dbVersion = '3.5.29';
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Leantime\Core\Configuration\Attributes;
use Attribute;
#[Attribute]
class LaravelConfig
{
public function __construct(
public string $config,
) {
//
}
}

View File

@@ -0,0 +1,583 @@
<?php
namespace Leantime\Core\Configuration;
use Leantime\Core\Configuration\Attributes\LaravelConfig;
/**
* Default Configuration Class
*/
class DefaultConfig
{
// General =====================================================================================
/**
* @var string Name of your site, can be changed later
*/
#[LaravelConfig('app.name')]
public string $sitename = 'OneBot';
/**
* @var string Default language
*/
#[LaravelConfig('app.locale')]
public string $language = 'en-US';
/**
* @var string Default logo path, can be changed later
*/
public string $logoPath = '/dist/images/logo.svg';
/**
* @var string Default logo URL use for printing (must be jpg or png format)
*/
public string $printLogoURL = '/dist/images/logo.jpg';
/**
* @var string Base URL, trailing slash not needed
*/
#[LaravelConfig('app.url')]
public string $appUrl = '';
/**
* @var string Base of application withotu trailing slash (used for cookies), e.g, /leantime
*/
public string $appDir = '';
/**
* @var bool Send anonymous data <a href='https://docs.leantime.io/#/using-leantime/company-settings?id=telemetry' target='_blank'>More Info</a>.
* No personally identifiable data will be sent and it will be impossible for us to track individual users.
*/
public bool $allowTelemetry = true;
/**
* @var string Default theme
*/
public string $defaultTheme = 'default';
/**
* @var string Primary Theme color
*/
public string $primarycolor;
/**
* @var string Secondary Theme Color
*/
public string $secondarycolor;
/**
* @var string Default timezone
*/
#[LaravelConfig('app.timezone')]
public string $defaultTimezone = 'America/Los_Angeles';
/**
* @var bool Enable to specifiy menu on a project by project basis
*/
public bool $enableMenuType = false;
/**
* @var bool|int Debug flag
*/
#[LaravelConfig('app.debug')]
public int|bool $debug = 0;
/**
* @var bool When true, a denied #[RequiresPermission] attribute blocks the request
* (403 web / JSON-RPC -32001). When false the central enforcer only LOGS the
* would-be denial (audit mode), so permission coverage can be rolled out and
* observed before being enforced. In-method $this->authorize() calls always
* enforce regardless of this flag.
*/
#[LaravelConfig('permissions.enforce')]
public bool $permissionsEnforce = true;
/**
* @var string editor used for code editing
*/
public string $editor = 'phpstorm';
/**
* @var string Application environment
*/
#[LaravelConfig('app.env')]
public string $env = 'production';
/**
* @var string Log Path
*/
public string $logPath = APP_ROOT.'/storage/logs/error.log';
/**
* @var bool Whether or not to enable the Poor Man's Cron fallback
*/
public bool $poorMansCron = true;
/**
* @var bool Don't show user/pass form on login?
*/
public bool $disableLoginForm = false;
// Database ====================================================================================
/**
* @var string Database host
*/
public string $dbHost = 'localhost';
/**
* @var string Database username
*/
public string $dbUser = '';
/**
* @var string Database password
*/
public string $dbPassword = '';
/**
* @var string Database name
*/
public string $dbDatabase = '';
/**
* @var string Database port
*/
public string $dbPort = '3306';
// Fileupload ==================================================================================
/**
* @var string Local relative path to store uploaded files (if not using S3)
*/
public string $userFilePath = 'userfiles/';
/**
* @var string Local relative path to store backup files, need permission to write
*/
public string $dbBackupPath = 'userfiles/';
// S3 configuration ============================================================================
/**
* @var bool Set to true if you want to use S3 instead of local files
*/
public bool $useS3 = false;
/**
* @var string S3 Key
*/
public string $s3Key = '';
/**
* @var string S3 Secret
*/
public string $s3Secret = '';
/**
* @var string S3 Bucket
*/
public string $s3Bucket = '';
/**
* @var bool false => https://[bucket].[endpoint] ; true => https://[endpoint]/[bucket]
*/
public bool $s3UsePathStyleEndpoint = false;
/**
* @var string S3 Region
*/
public string $s3Region = '';
/**
* @var string S3 Foldername within S3 (can be empty)
*/
public string $s3FolderName = '';
/**
* @var string|null S3 EndPoint S3 Compatible
*
* @see https://sfo2.digitaloceanspaces.com
*/
public ?string $s3EndPoint = null;
// Sessions ====================================================================================
/**
* @var string Salting sessions. Replace with a strong password
*/
#[LaravelConfig('app.key')]
public string $sessionPassword = '3evBlq9zdUEuzKvVJHWWx3QzsQhturBApxwcws2m';
/**
* @var int How many minutes after inactivity should we logout? 480min = 8hours
*/
public int $sessionExpiration = 480;
/**
* @var bool. Sets whether the cookie should only be served via https
*/
#[LaravelConfig('session.secure')]
public bool $sessionSecure = false;
// Email =======================================================================================
/**
* @var string Return email address
*/
public string $email = '';
/**
* @var bool Use SMTP? If set to false, the default php mail() function will be used
*/
public bool $useSMTP = false;
/**
* @var string SMTP host
*/
public string $smtpHosts = '';
/**
* @var bool SMTP use user/password authentication
*/
public bool $smtpAuth = true;
/**
* @var string SMTP username
*/
public string $smtpUsername = '';
/**
* @var string SMTP password
*/
public string $smtpPassword = '';
/**
* @var bool SMTP Enable TLS encryption automatically if a server supports it
*/
public bool $smtpAutoTLS = true;
/**
* @var string SMTP Security protocol (usually one of: TLS, SSL, STARTTLS)
*/
public string $smtpSecure = '';
/**
* @var bool SMTP Allow insecure SSL: Don't verify certificate, accept self-signed, etc.
*/
public bool $smtpSSLNoverify = false;
/**
* @var int SMTP Port (usually one of 25, 465, 587, 2526)
*/
public int $smtpPort = 587;
// ldap default settings (can be changed in company settings) ==================================
/**
* @var bool Set to true if you want to use LDAP
*/
public bool $useLdap = false;
/**
* @var string Select the correct directory type. Currently Supported: OL - OpenLdap, AD - Active Directory
*/
public string $ldapType = 'OL';
/**
* @var string LDAP host (FQDN)
*/
public string $ldapHost = '';
/**
* @var int LDAP port
*/
public int $ldapPort = 389;
/**
* @var string LDAP domain
*/
public string $ldapDomain = '';
/**
* @var string LDAP base DN
*/
public string $ldapUri = '';
/**
* @var string Location of users, example: CN=users,DC=example,DC=com
*/
public string $ldapDn = '';
/**
* @var string Default LDAP keys in your directory. Works for OL
*/
public string $ldapKeys = '{
"username":"uid",
"groups":"memberof",
"email":"mail",
"firstname":"displayname",
"lastname":"",
"phone":"",
"jobTitle":"title",
"jobLevel":"level",
"department":"department"
}';
// For AD use
/*
public $ldapKeys = '{
"username":"cn",
"groups":"memberof",
"email":"mail",
"firstname":"givenname",
"lastname":"sn",
"phone":"telephoneNumber",
"jobTitle":"title",
"jobLevel":"level",
"department":"department"
}';
*/
/**
* @var bool Create users
* Create user if not exists
*/
public bool $ldapCreateUser = false;
/**
* @var string Default role assignments upon first login. (Optional) Can be updated in user settings for each user
*/
public string $ldapLtGroupAssignments = '{
"5": {
"ltRole":"readonly",
"ldapRole":""
},
"10": {
"ltRole":"commenter",
"ldapRole":""
},
"20": {
"ltRole":"editor",
"ldapRole":""
},
"30": {
"ltRole":"manager",
"ldapRole":""
},
"40": {
"ltRole":"admin",
"ldapRole":""
},
"50": {
"ltRole":"owner",
"ldapRole":"administrators"
}
}';
// Default Leantime Role on creation. (set to editor)
/**
* @var int Default Leantime Role on creation. (set to editor)
*/
public int $ldapDefaultRoleKey = 20;
// Plugin Settings ==============================================================================
/**
* @var string Comma separated list of plugins that will always be loaded
*/
public string $plugins = '';
/**
* @var string The Url of the Marketplace
**/
public string $marketplaceUrl = 'https://marketplace.leantime.io/';
// OIDC Settings ================================================================================
/**
* @var bool Set to true if you want to use OIDC
*/
public bool $oidcEnable = false;
/**
* @var string OIDC Provider URL
*/
public string $oidcProviderUrl = '';
/**
* @var string OIDC Client ID
*/
public string $oidcClientId = '';
/**
* @var string OIDC Client Secret
*/
public string $oidcClientSecret = '';
/**
* @var string Custom Auto discover URL
*/
public string $oidcAutoDiscoverUrl = '';
/**
* @var string OIDC Auth URL
*/
public string $oidcAuthUrl = '';
/**
* @var string OIDC Token URL
*/
public string $oidcTokenUrl = '';
/**
* @var string OIDC JWKS URL
*/
public string $oidcJwksUrl = '';
/**
* @var string OIDC User Info URL
*/
public string $oidcUserInfoUrl = '';
/**
* @var string OIDC Certificate String
*/
public string $oidcCertificateString = '';
/**
* @var string OIDC Certificate File
*/
public string $oidcCertificateFile = '';
/**
* @var string OIDC Scopes
*/
public string $oidcScopes = 'openid profile email';
/**
* @var bool create user
*
* Create user if not exists
*/
public bool $oidcCreateUser = false;
/**
* @var int OIDC
*
* Default Role for new users
*/
public int $oidcDefaultRole = 20;
/**
* @var string OIDC Field Email
*/
public string $oidcFieldEmail = 'email';
/**
* @var string OIDC Field First Name
*/
public string $oidcFieldFirstName = 'given_name';
/**
* @var string OIDC Field Last Name
*/
public string $oidcFieldLastName = 'family_name';
/**
* @var string OIDC Field Phone
*/
public string $oidcFieldPhone = '';
/**
* @var string OIDC Field Job Title
*/
public string $oidcFieldJobtitle = '';
/**
* @var string OIDC Field Job Level
*/
public string $oidcFieldJoblevel = '';
/**
* @var string OIDC Field Department
*/
public string $oidcFieldDepartment = '';
// Redis Settings ===============================================================================
/**
* @var bool Set to true if you want to use Redis
*/
public bool $useRedis = false;
/**
* @var bool Set to true if you want to use a redis cluster
*/
public bool $useCluster = false;
/**
* @var string Redis URL
*/
#[LaravelConfig('redis.default.url')]
public string $redisUrl = '';
/**
* @var string Redis Host
*/
#[LaravelConfig('redis.default.host')]
public string $redisHost = '127.0.0.1';
/**
* @var string Redis Port
*/
#[LaravelConfig('redis.default.port')]
public string $redisPort = '6379';
/**
* @var string Redis Password
*/
#[LaravelConfig('redis.default.password')]
public string $redisPassword = '';
/**
* @var string Redis Password
*/
#[LaravelConfig('redis.default.tls')]
public string $redisScheme = 'tls';
// Security/Rate Limiting Settings ===============================================================================
/**
* @var string trusted Proxies
*/
public string $trustedProxies = '127.0.0.1,REMOTE_ADDR';
/**
* @var int rate limit on all requests
*/
public int $ratelimitGeneral = 2000;
/**
* @var int rate limit on API requests (per user+IP per minute). 120 = 2 req/s sustained —
* enough for mobile-app sync bursts and integration polling while still catching
* runaway scripts; in line with comparable tools (GitHub ~83/min, Jira ~100/min).
*/
public int $ratelimitApi = 120;
/**
* @var int rate limit on auth requests
*/
public int $ratelimitAuth = 20;
/**
* @var int rate limit on MCP endpoint requests (per user+IP per minute). Higher than the API
* limit because agentic LLM clients burst many parallel tool calls per turn.
*/
public int $ratelimitMcp = 300;
/**
* @var int rate limit on signup + user-invite POSTs (per IP per minute). These endpoints
* send email and provision resources, so they get a tight budget (invite-spam abuse).
*/
public int $ratelimitSignup = 5;
/**
* @var int maximum user invites per inviting user per hour
*/
public int $ratelimitInvitesUser = 10;
/**
* @var int maximum user invites per installation per day. Raise for legitimate bulk
* onboarding (CSV/directory imports).
*/
public int $ratelimitInvitesTenant = 30;
}

View File

@@ -0,0 +1,183 @@
<?php
namespace Leantime\Core\Configuration;
use ArrayAccess;
use Exception;
use Illuminate\Config\Repository;
use Illuminate\Contracts\Config\Repository as ConfigContract;
use Illuminate\Support\Str;
use Leantime\Config\Config;
use Symfony\Component\Yaml\Yaml;
/**
* environment - class To handle environment variables
*/
class Environment extends Repository implements ArrayAccess, ConfigContract
{
// Config Files ===============================================================================
private ?object $yaml;
private ?Config $phpConfig;
/**
* @var array list of legacy mappings
*
* @todo warn about key changes after deprecating config/configuration.php
* @todo remove this after removing support for config/configuration.php
*/
private const LEGACY_MAPPINGS = [
'printLogoUrl' => 'LEAN_PRINT_LOGO_URL',
'primarycolor' => 'LEAN_PRIMARY_COLOR',
'secondarycolor' => 'LEAN_SECONDARY_COLOR',
'email' => 'LEAN_EMAIL_RETURN',
'useSMTP' => 'LEAN_EMAIL_USE_SMTP',
'smtpHosts' => 'LEAN_EMAIL_SMTP_HOSTS',
'smtpAuth' => 'LEAN_EMAIL_SMTP_AUTH',
'smtpUsername' => 'LEAN_EMAIL_SMTP_USERNAME',
'smtpPassword' => 'LEAN_EMAIL_SMTP_PASSWORD',
'smtpAutoTLS' => 'LEAN_EMAIL_SMTP_AUTO_TLS',
'smtpSecure' => 'LEAN_EMAIL_SMTP_SECURE',
'smtpPort' => 'LEAN_EMAIL_SMTP_PORT',
'smtpSSLNoverify' => 'LEAN_EMAIL_SMTP_SSLNOVERIFY',
'useLdap' => 'LEAN_LDAP_USE_LDAP',
'ldapType' => 'LEAN_LDAP_LDAP_TYPE',
'ldapLtGroupAssignments' => 'LEAN_LDAP_GROUP_ASSIGNMENT',
'ldapDomain' => 'LEAN_LDAP_LDAP_DOMAIN',
'oidcClientId' => 'LEAN_OIDC_CLIENT_ID',
'oidcClientSecret' => 'LEAN_OIDC_CLIENT_SECRET',
'oidcAutoDiscoverUrl' => 'LEAN_OIDC_AUTO_DISCOVER',
'oidcAuthUrl' => 'LEAN_OIDC_AUTH_URL_OVERRIDE',
'oidcTokenUrl' => 'LEAN_OIDC_TOKEN_URL_OVERRIDE',
'oidcJwksUrl' => 'LEAN_OIDC_JWKS_URL_OVERRIDE',
'oidcUserInfoUrl' => 'LEAN_OIDC_USERINFO_URL_OVERRIDE',
'oidcFieldFirstName' => 'LEAN_OIDC_FIELD_FIRSTNAME',
'oidcFieldLastName' => 'LEAN_OIDC_FIELD_LASTNAME',
'redisURL' => 'LEAN_REDIS_URL',
];
/**
* environment constructor.
*
* @throws Exception
*/
public function __construct(array $items = [])
{
if (! empty($items) && is_array($items)) {
$this->items = $items;
}
$defaultConfiguration = new DefaultConfig;
/* PHP */
$this->phpConfig = null;
if (file_exists($phpConfigFile = APP_ROOT.'/config/configuration.php')) {
require_once $phpConfigFile;
if (! class_exists(Config::class)) {
throw new Exception('We found a php configuration file but the class cannot be instantiated. Please check the configuration file for namespace and class name. You can use the configuration.sample.php as a template. See https://github.com/leantime/leantime/releases/tag/v2.4-beta-2 for more details.');
}
$this->phpConfig = new Config;
$configVars = get_class_vars(Config::class);
foreach (array_keys($configVars) as $propertyName) {
$envVarName = self::LEGACY_MAPPINGS[$propertyName] ?? 'LEAN_'.Str::of($propertyName)->snake()->upper()->toString();
putenv($envVarName.'='.$configVars[$propertyName]);
}
}
$defaultConfigurationProperties = get_class_vars($defaultConfiguration::class);
foreach (array_keys($defaultConfigurationProperties) as $propertyName) {
$type = gettype($defaultConfigurationProperties[$propertyName]);
$type = $type == 'NULL' ? 'string' : $type;
$this->set($propertyName, $this->environmentHelper(
envVar: self::LEGACY_MAPPINGS[$propertyName] ?? 'LEAN_'.Str::of($propertyName)->snake()->upper()->toString(),
default: $defaultConfigurationProperties[$propertyName],
dataType: $type,
));
}
}
/**
* environmentHelper - helper function to get a value from the environment
*/
private function environmentHelper(string $envVar, mixed $default, string $dataType = 'string'): mixed
{
/**
* Basically, here, we are doing the fetch order of
* environment -> .env file -> yaml file -> user default -> leantime default
* This allows us to use any one or a combination of those methods to configure leantime.
*/
$found = $default;
$found = $this->tryGetFromPhp($envVar, $found) ?? $found;
$found = $this->tryGetFromEnvironment($envVar, $found) ?? $found;
// we need to check to see if we need to convert the found data
return match ($dataType) {
'string' => $found,
'boolean' => filter_var($found, FILTER_VALIDATE_BOOLEAN),
'number' => (int) ($found),
default => $found,
};
}
private function tryGetFromPhp(string $envVar, mixed $currentValue): mixed
{
if ($this->phpConfig) {
$key = array_search($envVar, self::LEGACY_MAPPINGS) ?: Str::of($envVar)->replace('LEAN_', '')->lower()->camel()->toString();
return $this->phpConfig->$key ?? $currentValue;
}
return null;
}
/**
* tryGetFromEnvironment - try to get a value from the environment
*/
private function tryGetFromEnvironment(string $envVar, mixed $currentValue): mixed
{
return $_ENV[$envVar] ?? env($envVar) ?? $currentValue;
}
/**
* Dynamically access the configuration using object syntax.
*/
public function __get(string $key): mixed
{
return $this->get($key);
}
/**
* Dynamically set the configuration using object syntax.
*/
public function __set(string $key, mixed $value): void
{
$this->set($key, $value);
}
/**
* Dynamically check if a configuration option is set using object syntax.
*/
public function __isset(string $key): bool
{
return $this->has($key) && $this->get($key) !== null;
}
/**
* Dynamically unset a configuration option using object syntax.
*/
public function __unset(string $key): void
{
$this->set($key, null);
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Leantime\Core\Configuration;
use Illuminate\Support\ServiceProvider;
use Leantime\Core\Events\DispatchesEvents;
class EnvironmentServiceProvider extends ServiceProvider
{
use DispatchesEvents;
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->app->singleton(
\Leantime\Core\Configuration\AppSettings::class, \Leantime\Core\Configuration\AppSettings::class);
$this->app->singleton(
\Leantime\Core\Configuration\Environment::class, \Leantime\Core\Configuration\Environment::class);
}
public function boot()
{
self::dispatchEvent('config_initialized');
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Leantime\Core\Configuration;
/**
* Enum class Environment options
*
* Enum representing the available environment configs
*/
enum EnvironmentsEnum: string
{
/**
* Set to dev environment
*/
case Dev = 'dev';
/**
* Set to staging environment
*/
case Staging = 'staging';
/**
* Set to oss release environment
*/
case Oss = 'oss';
/**
* Set to prod environment
*/
case Production = 'production';
}

File diff suppressed because it is too large Load Diff