Merge branch 'main' into codex/gitea-repository-read-check
This commit is contained in:
@@ -0,0 +1,49 @@
|
|||||||
|
# Include any files or directories that you don't want to be copied to your
|
||||||
|
# container here (e.g., local build artifacts, temporary files, etc.).
|
||||||
|
#
|
||||||
|
# For more help, visit the .dockerignore file reference guide at
|
||||||
|
# https://docs.docker.com/go/build-context-dockerignore/
|
||||||
|
|
||||||
|
**/.classpath
|
||||||
|
**/.dockerignore
|
||||||
|
**/.env
|
||||||
|
**/.git
|
||||||
|
**/.gitignore
|
||||||
|
**/.gitattributes
|
||||||
|
**/.project
|
||||||
|
**/.settings
|
||||||
|
**/.toolstarget
|
||||||
|
**/.vs
|
||||||
|
**/.vscode
|
||||||
|
**/.next
|
||||||
|
**/.cache
|
||||||
|
**/*.*proj.user
|
||||||
|
**/*.dbmdl
|
||||||
|
**/*.jfm
|
||||||
|
**/docker-compose*
|
||||||
|
**/compose.y*ml
|
||||||
|
**/compose.*.y*ml
|
||||||
|
!**/composer.json
|
||||||
|
!**/composer.lock
|
||||||
|
**/Dockerfile*
|
||||||
|
**/node_modules
|
||||||
|
**/npm-debug.log
|
||||||
|
**/obj
|
||||||
|
**/secrets.dev.yaml
|
||||||
|
**/values.dev.yaml
|
||||||
|
vendor
|
||||||
|
**/.DS_Store
|
||||||
|
.editorconfig
|
||||||
|
.env.example
|
||||||
|
.env.mariadb
|
||||||
|
.env.mariadb.example
|
||||||
|
.env.production
|
||||||
|
LICENSE
|
||||||
|
README.md
|
||||||
|
|
||||||
|
# Ignore development-related files and directories
|
||||||
|
# For development environment, we will use mount bind to mount the source code to the container
|
||||||
|
# So we don't need to copy the source code to the container for development
|
||||||
|
tests/
|
||||||
|
phpunit.xml
|
||||||
|
phpunit.xml.dist
|
||||||
+19
-50
@@ -1,65 +1,34 @@
|
|||||||
APP_NAME=Laravel
|
APP_NAME=kanbangit
|
||||||
APP_ENV=local
|
APP_ENV=local
|
||||||
APP_KEY=
|
APP_KEY=
|
||||||
APP_DEBUG=true
|
APP_DEBUG=true
|
||||||
|
APP_TIMEZONE=Europe/Berlin
|
||||||
APP_URL=http://localhost
|
APP_URL=http://localhost
|
||||||
|
API_KEY=
|
||||||
|
|
||||||
APP_LOCALE=en
|
APP_LOCALE=en
|
||||||
APP_FALLBACK_LOCALE=en
|
APP_FALLBACK_LOCALE=en
|
||||||
APP_FAKER_LOCALE=en_US
|
APP_FAKER_LOCALE=en_US
|
||||||
|
|
||||||
APP_MAINTENANCE_DRIVER=file
|
LOG_STACK=daily
|
||||||
# APP_MAINTENANCE_STORE=database
|
|
||||||
|
|
||||||
# PHP_CLI_SERVER_WORKERS=4
|
DB_CONNECTION=mariadb
|
||||||
|
DB_HOST=kanbangit-db
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_DATABASE=kanbangit
|
||||||
|
DB_USERNAME=
|
||||||
|
DB_PASSWORD=
|
||||||
|
|
||||||
BCRYPT_ROUNDS=12
|
#Default
|
||||||
|
MAIL_MAILER=smtp
|
||||||
LOG_CHANNEL=stack
|
MAIL_HOST=mail.example.de
|
||||||
LOG_STACK=single
|
MAIL_PORT=587
|
||||||
LOG_DEPRECATIONS_CHANNEL=null
|
MAIL_USERNAME=no-reply@example.com
|
||||||
LOG_LEVEL=debug
|
MAIL_PASSWORD=
|
||||||
|
MAIL_ENCRYPTION=tls
|
||||||
DB_CONNECTION=sqlite
|
MAIL_FROM_ADDRESS=no-reply@example.com
|
||||||
# DB_HOST=127.0.0.1
|
|
||||||
# DB_PORT=3306
|
|
||||||
# DB_DATABASE=laravel
|
|
||||||
# DB_USERNAME=root
|
|
||||||
# DB_PASSWORD=
|
|
||||||
|
|
||||||
SESSION_DRIVER=database
|
|
||||||
SESSION_LIFETIME=120
|
|
||||||
SESSION_ENCRYPT=false
|
|
||||||
SESSION_PATH=/
|
|
||||||
SESSION_DOMAIN=null
|
|
||||||
|
|
||||||
BROADCAST_CONNECTION=log
|
|
||||||
FILESYSTEM_DISK=local
|
|
||||||
QUEUE_CONNECTION=database
|
|
||||||
|
|
||||||
CACHE_STORE=database
|
|
||||||
# CACHE_PREFIX=
|
|
||||||
|
|
||||||
MEMCACHED_HOST=127.0.0.1
|
|
||||||
|
|
||||||
REDIS_CLIENT=phpredis
|
|
||||||
REDIS_HOST=127.0.0.1
|
|
||||||
REDIS_PASSWORD=null
|
|
||||||
REDIS_PORT=6379
|
|
||||||
|
|
||||||
MAIL_MAILER=log
|
|
||||||
MAIL_SCHEME=null
|
|
||||||
MAIL_HOST=127.0.0.1
|
|
||||||
MAIL_PORT=2525
|
|
||||||
MAIL_USERNAME=null
|
|
||||||
MAIL_PASSWORD=null
|
|
||||||
MAIL_FROM_ADDRESS="hello@example.com"
|
|
||||||
MAIL_FROM_NAME="${APP_NAME}"
|
MAIL_FROM_NAME="${APP_NAME}"
|
||||||
|
|
||||||
AWS_ACCESS_KEY_ID=
|
FILESYSTEM_DISK=public
|
||||||
AWS_SECRET_ACCESS_KEY=
|
|
||||||
AWS_DEFAULT_REGION=us-east-1
|
|
||||||
AWS_BUCKET=
|
|
||||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
|
||||||
|
|
||||||
VITE_APP_NAME="${APP_NAME}"
|
VITE_APP_NAME="${APP_NAME}"
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
MARIADB_DATABASE=kanbangit
|
||||||
|
MARIADB_USER=kanbangit
|
||||||
|
MARIADB_PASSWORD=
|
||||||
|
MARIADB_ROOT_PASSWORD=
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
.env
|
.env
|
||||||
.env.backup
|
.env.backup
|
||||||
|
.env.mariadb
|
||||||
.env.production
|
.env.production
|
||||||
.phpactor.json
|
.phpactor.json
|
||||||
.phpunit.result.cache
|
.phpunit.result.cache
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use App\Models\BoardColumn;
|
|||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
@@ -42,12 +43,13 @@ class ProjectBoardSettingsController extends Controller
|
|||||||
$this->projectPermissionService->userCanManageProjectAccess($request->user(), $project),
|
$this->projectPermissionService->userCanManageProjectAccess($request->user(), $project),
|
||||||
403,
|
403,
|
||||||
);
|
);
|
||||||
|
$validated = $request->validated();
|
||||||
|
|
||||||
$desiredPosition = $request->position() ?? $project->boardColumns()->count() + 1;
|
$desiredPosition = Arr::get($validated, 'position') ?? $project->boardColumns()->count() + 1;
|
||||||
|
|
||||||
$boardColumn = DB::transaction(function () use ($project, $request, $desiredPosition): BoardColumn {
|
$boardColumn = DB::transaction(function () use ($project, $validated, $desiredPosition): BoardColumn {
|
||||||
$boardColumn = $project->boardColumns()->create([
|
$boardColumn = $project->boardColumns()->create([
|
||||||
'name' => $request->validated('name'),
|
'name' => Arr::get($validated, 'name'),
|
||||||
'position' => $project->boardColumns()->count() + 1,
|
'position' => $project->boardColumns()->count() + 1,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -69,13 +71,14 @@ class ProjectBoardSettingsController extends Controller
|
|||||||
$this->projectPermissionService->userCanManageProjectAccess($request->user(), $project),
|
$this->projectPermissionService->userCanManageProjectAccess($request->user(), $project),
|
||||||
403,
|
403,
|
||||||
);
|
);
|
||||||
|
$validated = $request->validated();
|
||||||
|
|
||||||
DB::transaction(function () use ($boardColumn, $project, $request): void {
|
DB::transaction(function () use ($boardColumn, $project, $validated): void {
|
||||||
$boardColumn->update([
|
$boardColumn->update([
|
||||||
'name' => $request->validated('name'),
|
'name' => Arr::get($validated, 'name'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->reorderColumns($project, $boardColumn, $request->position());
|
$this->reorderColumns($project, $boardColumn, Arr::get($validated, 'position'));
|
||||||
});
|
});
|
||||||
|
|
||||||
return to_route('projects.settings.board', $project)
|
return to_route('projects.settings.board', $project)
|
||||||
|
|||||||
@@ -59,6 +59,17 @@ class ProjectTicketLinkController extends Controller
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$this->createTicketWorklogEntry->handle(
|
||||||
|
$linkedTicket,
|
||||||
|
$request->user(),
|
||||||
|
'link_updated',
|
||||||
|
"Linked {$ticket->ticket_key} as {$ticketLink->ticketLinkType->label}.",
|
||||||
|
[
|
||||||
|
'linked_ticket_key' => $ticket->ticket_key,
|
||||||
|
'ticket_link_type' => $ticketLink->ticketLinkType->label,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
return $ticketLink;
|
return $ticketLink;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -103,6 +114,17 @@ class ProjectTicketLinkController extends Controller
|
|||||||
'ticket_link_type' => $label,
|
'ticket_link_type' => $label,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$this->createTicketWorklogEntry->handle(
|
||||||
|
$linkedTicket,
|
||||||
|
$request->user(),
|
||||||
|
'link_removed',
|
||||||
|
"Removed {$label} link to {$ticket->ticket_key}.",
|
||||||
|
[
|
||||||
|
'linked_ticket_key' => $ticket->ticket_key,
|
||||||
|
'ticket_link_type' => $label,
|
||||||
|
],
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return to_route('projects.tickets.show', [$project, $ticket])
|
return to_route('projects.tickets.show', [$project, $ticket])
|
||||||
|
|||||||
@@ -27,15 +27,4 @@ class StoreBoardColumnRequest extends FormRequest
|
|||||||
'position' => ['nullable', 'integer', 'min:1'],
|
'position' => ['nullable', 'integer', 'min:1'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function position(): ?int
|
|
||||||
{
|
|
||||||
$position = $this->validated('position');
|
|
||||||
|
|
||||||
if ($position === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int) $position;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,9 +27,4 @@ class UpdateBoardColumnRequest extends FormRequest
|
|||||||
'position' => ['required', 'integer', 'min:1'],
|
'position' => ['required', 'integer', 'min:1'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function position(): int
|
|
||||||
{
|
|
||||||
return (int) $this->validated('position');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use Carbon\CarbonImmutable;
|
|||||||
use Illuminate\Support\Facades\Date;
|
use Illuminate\Support\Facades\Date;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Gate;
|
use Illuminate\Support\Facades\Gate;
|
||||||
|
use Illuminate\Support\Facades\URL;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use Illuminate\Validation\Rules\Password;
|
use Illuminate\Validation\Rules\Password;
|
||||||
|
|
||||||
@@ -25,6 +26,11 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
*/
|
*/
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if ($this->app->environment() !== 'local') {
|
||||||
|
URL::forceScheme('https');
|
||||||
|
}
|
||||||
|
|
||||||
$this->configureAuthorization();
|
$this->configureAuthorization();
|
||||||
$this->configureDefaults();
|
$this->configureDefaults();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ return new class extends Migration
|
|||||||
$table->foreignId('ticket_id')->constrained()->cascadeOnDelete();
|
$table->foreignId('ticket_id')->constrained()->cascadeOnDelete();
|
||||||
$table->foreignId('ticket_field_definition_id')->constrained()->cascadeOnDelete();
|
$table->foreignId('ticket_field_definition_id')->constrained()->cascadeOnDelete();
|
||||||
$table->foreignId('value_user_id')->nullable()->constrained('users')->nullOnDelete();
|
$table->foreignId('value_user_id')->nullable()->constrained('users')->nullOnDelete();
|
||||||
$table->decimal('value_number', 10, 2)->nullable();
|
$table->decimal('value_number', 16, 4)->nullable();
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
|
|
||||||
$table->unique(['ticket_id', 'ticket_field_definition_id']);
|
$table->unique(['ticket_id', 'ticket_field_definition_id']);
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ return new class extends Migration
|
|||||||
'provider',
|
'provider',
|
||||||
'base_url',
|
'base_url',
|
||||||
'provider_repository_id',
|
'provider_repository_id',
|
||||||
]);
|
], 'repo_connections_project_provider_repo_unique');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
services:
|
||||||
|
web:
|
||||||
|
container_name: kanbangit-web
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: ./docker/nginx/Dockerfile
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "8080:80"
|
||||||
|
volumes:
|
||||||
|
- kanbangit-storage:/var/www/storage:ro
|
||||||
|
- kanbangit-public-assets:/var/www/public/build:ro
|
||||||
|
env_file:
|
||||||
|
- .env.production
|
||||||
|
networks:
|
||||||
|
- kanbangit-web
|
||||||
|
depends_on:
|
||||||
|
php-fpm:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
php-fpm:
|
||||||
|
container_name: kanbangit-php-fpm
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: ./docker/php-fpm/Dockerfile
|
||||||
|
target: production
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- kanbangit-storage:/var/www/storage
|
||||||
|
- kanbangit-public-assets:/var/www/public/build
|
||||||
|
env_file:
|
||||||
|
- .env.production
|
||||||
|
networks:
|
||||||
|
- kanbangit-web
|
||||||
|
- kanbangit-db
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "php-fpm-healthcheck || exit 1"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
depends_on:
|
||||||
|
mariadb:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
mariadb:
|
||||||
|
container_name: kanbangit-db
|
||||||
|
image: mariadb:11.4
|
||||||
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- .env.mariadb
|
||||||
|
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||||
|
volumes:
|
||||||
|
- mariadb-data-production:/var/lib/mysql
|
||||||
|
networks:
|
||||||
|
- kanbangit-db
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD", "healthcheck.sh", "--connect", "--innodb_initialized" ]
|
||||||
|
start_period: 10s
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
networks:
|
||||||
|
kanbangit-db:
|
||||||
|
kanbangit-web:
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mariadb-data-production:
|
||||||
|
kanbangit-storage:
|
||||||
|
kanbangit-public-assets:
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
FROM php:8.4-cli-bookworm AS builder
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
curl ca-certificates git unzip \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
|
||||||
|
&& apt-get update && apt-get install -y --no-install-recommends nodejs \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
RUN curl -sS https://getcomposer.org/installer | php -- \
|
||||||
|
--install-dir=/usr/local/bin --filename=composer
|
||||||
|
WORKDIR /var/www
|
||||||
|
COPY . /var/www
|
||||||
|
RUN composer install --no-dev --prefer-dist --no-interaction --no-progress --optimize-autoloader
|
||||||
|
RUN npm ci && npm run build
|
||||||
|
|
||||||
|
FROM nginx:alpine
|
||||||
|
COPY ./docker/nginx/nginx.conf /etc/nginx/nginx.conf
|
||||||
|
COPY --from=builder /var/www/public /var/www/public
|
||||||
|
WORKDIR /var/www/public
|
||||||
|
EXPOSE 80
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
sendfile on;
|
||||||
|
keepalive_timeout 65;
|
||||||
|
client_max_body_size 100m;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name localhost;
|
||||||
|
root /var/www/public;
|
||||||
|
index index.php index.html;
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.php?$query_string;
|
||||||
|
}
|
||||||
|
location ^~ /storage/ {
|
||||||
|
alias /var/www/storage/app/public/;
|
||||||
|
try_files $uri =404;
|
||||||
|
access_log off;
|
||||||
|
expires 7d;
|
||||||
|
add_header Cache-Control "public";
|
||||||
|
}
|
||||||
|
location ~ \.php$ {
|
||||||
|
fastcgi_pass php-fpm:9000;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
include fastcgi_params;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
FROM php:8.4-fpm AS builder
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
curl \
|
||||||
|
unzip \
|
||||||
|
libpq-dev \
|
||||||
|
libonig-dev \
|
||||||
|
libssl-dev \
|
||||||
|
libxml2-dev \
|
||||||
|
libcurl4-openssl-dev \
|
||||||
|
libicu-dev \
|
||||||
|
libzip-dev \
|
||||||
|
libmagickwand-dev \
|
||||||
|
libpng-dev \
|
||||||
|
libjpeg-dev \
|
||||||
|
libfreetype6-dev \
|
||||||
|
&& docker-php-ext-install -j$(nproc) \
|
||||||
|
pdo_mysql \
|
||||||
|
pdo_pgsql \
|
||||||
|
pgsql \
|
||||||
|
opcache \
|
||||||
|
intl \
|
||||||
|
zip \
|
||||||
|
bcmath \
|
||||||
|
soap \
|
||||||
|
&& docker-php-ext-configure gd --with-jpeg --with-freetype \
|
||||||
|
&& docker-php-ext-install -j$(nproc) gd \
|
||||||
|
&& pecl install imagick \
|
||||||
|
&& docker-php-ext-enable imagick \
|
||||||
|
&& apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
WORKDIR /var/www
|
||||||
|
COPY . /var/www
|
||||||
|
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
|
||||||
|
&& composer install --no-dev --optimize-autoloader --no-interaction --no-progress --prefer-dist
|
||||||
|
FROM php:8.4-fpm AS production
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
libpq-dev \
|
||||||
|
libicu-dev \
|
||||||
|
libzip-dev \
|
||||||
|
libfcgi-bin \
|
||||||
|
procps \
|
||||||
|
libmagickwand-dev \
|
||||||
|
&& apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
RUN curl -o /usr/local/bin/php-fpm-healthcheck \
|
||||||
|
https://raw.githubusercontent.com/renatomefi/php-fpm-healthcheck/master/php-fpm-healthcheck \
|
||||||
|
&& chmod +x /usr/local/bin/php-fpm-healthcheck
|
||||||
|
COPY ./docker/php-fpm/entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||||
|
RUN chmod +x /usr/local/bin/entrypoint.sh
|
||||||
|
COPY ./storage /var/www/storage-init
|
||||||
|
COPY --from=builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/
|
||||||
|
COPY --from=builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/
|
||||||
|
COPY --from=builder /usr/local/bin/docker-php-ext-* /usr/local/bin/
|
||||||
|
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
|
||||||
|
RUN printf '%s\n' \
|
||||||
|
'[global]' \
|
||||||
|
'daemonize = no' \
|
||||||
|
'' \
|
||||||
|
'[www]' \
|
||||||
|
'pm.status_path = /status' \
|
||||||
|
> /usr/local/etc/php-fpm.d/zz-docker.conf
|
||||||
|
COPY --from=builder /var/www /var/www
|
||||||
|
WORKDIR /var/www
|
||||||
|
RUN chown -R www-data:www-data /var/www
|
||||||
|
USER www-data
|
||||||
|
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
||||||
|
EXPOSE 9000
|
||||||
|
CMD ["php-fpm"]
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
if [ ! "$(ls -A /var/www/storage)" ]; then
|
||||||
|
echo "Initializing storage directory..."
|
||||||
|
cp -R /var/www/storage-init/. /var/www/storage
|
||||||
|
chown -R www-data:www-data /var/www/storage
|
||||||
|
fi
|
||||||
|
rm -rf /var/www/storage-init
|
||||||
|
php artisan config:cache
|
||||||
|
php artisan route:cache
|
||||||
|
exec "$@"
|
||||||
@@ -10,6 +10,8 @@ KanbanGit is intended to become a self-hostable project management application w
|
|||||||
|
|
||||||
The product centers around projects. Each project owns its own board configuration, backlog, ticket metadata, ticket hierarchy, and repository connections. The architecture must support application-wide administration plus project-scoped permissions that can be assigned to users and teams.
|
The product centers around projects. Each project owns its own board configuration, backlog, ticket metadata, ticket hierarchy, and repository connections. The architecture must support application-wide administration plus project-scoped permissions that can be assigned to users and teams.
|
||||||
|
|
||||||
|
First Mockup https://stitch.withgoogle.com/projects/353263382234239260
|
||||||
|
|
||||||
## Documentation map
|
## Documentation map
|
||||||
|
|
||||||
- [Product Spec](product-spec.md) describes expected product behavior, user-visible rules, and scope boundaries.
|
- [Product Spec](product-spec.md) describes expected product behavior, user-visible rules, and scope boundaries.
|
||||||
|
|||||||
@@ -2,5 +2,70 @@
|
|||||||
|
|
||||||
### Ticket
|
### Ticket
|
||||||
|
|
||||||
|
- general:
|
||||||
|
- new skill, user resources
|
||||||
|
- ueberall $array['''] ersetzen durch Arr::get()
|
||||||
|
- (mehr über middlewares steuern)
|
||||||
|
- why uses everywhere forceFill?
|
||||||
|
- $request->validated('xxx') ersetzten durch:
|
||||||
|
- ``` $validated = $request->validate(); $xxx = Arr:get($validated, 'xxx'); ```
|
||||||
|
- oder: ```$request->position()```, methoden entfernen, wie:
|
||||||
|
```
|
||||||
|
public function position(): ?int {
|
||||||
|
position = this->validated('position');
|
||||||
|
|
||||||
|
if ($position === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int) $position;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- app/Http/Requests/UpdateProjectTicketRequest.php and other ProjectTicket only to Ticket...
|
||||||
|
- mehrere actions/quereys in services auslagern
|
||||||
|
|
||||||
- 02:
|
- 02:
|
||||||
- Delete Projekt as Admin
|
- Delete Projekt as Admin
|
||||||
|
- 03
|
||||||
|
- permission models? eg.g syncUserPermissions uses only db table
|
||||||
|
- mit morph auf perm user (z.b. team oder user)
|
||||||
|
- Project Permissions Service
|
||||||
|
- syncTeamMembers(Team $team, array $userIds): void → ist wrong here
|
||||||
|
- gate statt has permission methods
|
||||||
|
- permission for creating teams without beeing admind -> teamowner
|
||||||
|
- team roles
|
||||||
|
- 05
|
||||||
|
- ticket type remove build in
|
||||||
|
- → add is parentBool to ticketType -> remove system_key (maybe add slug)
|
||||||
|
- auto generate one parent one task on new project as now
|
||||||
|
- disable delete last child ticket type
|
||||||
|
- initializeProjectTicketMetadata nur beim init benutzen
|
||||||
|
- every form request removed unnecessary methods: like position in StoreProjectNamedSettingRequest
|
||||||
|
- use Arr:get(\$validated, 'value'). instead of these messages or \$request->value
|
||||||
|
- Made ticket priority more static(change to has one) in ticket, but only show if there are more than one configured.
|
||||||
|
- made story points more static(change to table field in table), but only show if checked in project settings → new value on Project: hasStoryPoints
|
||||||
|
- more ticket field types (string, string array, boolean, precision (double))
|
||||||
|
- abortUnlessCanManage(xxx) entfernen, auch gate benutzen (ProjectTicketMetadataSettingsController)
|
||||||
|
- ticket assignee fest einbauen in ticket
|
||||||
|
- 06
|
||||||
|
- add Ticket Status
|
||||||
|
- ticketstatus can have one or more board columns
|
||||||
|
- one board cloumn cant be in two or more ticket statuses
|
||||||
|
- replace board_colum_id on ticket with ticket status id
|
||||||
|
- ticket status has an n:m relation in which relatin the ticket can switch
|
||||||
|
- (ui friendly example all following or select)
|
||||||
|
- change Project Backlog Controller → Project Controller
|
||||||
|
- remove store from project board controller and route projects/{project}/backlog, change to ticket controller, create
|
||||||
|
- 08
|
||||||
|
- worklog überarbeiten → type als worklog enum → json je nach enum type
|
||||||
|
- UpdateProjectTicket aufräumen
|
||||||
|
- 09
|
||||||
|
- app/Http/Controllers/ProjectTicketController.php parent ids anderes queren. nicht alle child ids berechnen und excluden -> immer parent vom parent bis null
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- later:
|
||||||
|
- if deleting ticket type select a new ticket type which all existing ticket should get or select delete all tickets of that type
|
||||||
|
- repository_connections check base Url maybe unnessecary?
|
||||||
|
- Git listBranches replace baseUrl, owner, rperoName with GItReprository
|
||||||
|
- replace Kanbangit with env App Name
|
||||||
@@ -22,10 +22,10 @@ import {
|
|||||||
SidebarMenuButton,
|
SidebarMenuButton,
|
||||||
SidebarMenuItem,
|
SidebarMenuItem,
|
||||||
} from '@/components/ui/sidebar';
|
} from '@/components/ui/sidebar';
|
||||||
|
import { dashboard } from '@/routes';
|
||||||
import { index as adminTeams } from '@/routes/admin/teams';
|
import { index as adminTeams } from '@/routes/admin/teams';
|
||||||
import { index as adminUsers } from '@/routes/admin/users';
|
import { index as adminUsers } from '@/routes/admin/users';
|
||||||
import { index as projectsIndex } from '@/routes/projects/index';
|
import { index as projectsIndex } from '@/routes/projects';
|
||||||
import { dashboard } from '@/routes/index';
|
|
||||||
import type { NavItem } from '@/types';
|
import type { NavItem } from '@/types';
|
||||||
|
|
||||||
const page = usePage();
|
const page = usePage();
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ watch(
|
|||||||
async (isOpen) => {
|
async (isOpen) => {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
resetModalState();
|
resetModalState();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Form, Head } from '@inertiajs/vue3';
|
import { Form, Head } from '@inertiajs/vue3';
|
||||||
import { Shield, Users } from 'lucide-vue-next';
|
import { Shield } from 'lucide-vue-next';
|
||||||
import TeamController from '@/actions/App/Http/Controllers/TeamController';
|
import TeamController from '@/actions/App/Http/Controllers/TeamController';
|
||||||
import Heading from '@/components/Heading.vue';
|
import Heading from '@/components/Heading.vue';
|
||||||
import InputError from '@/components/InputError.vue';
|
import InputError from '@/components/InputError.vue';
|
||||||
|
|||||||
Vendored
-1
@@ -19,7 +19,6 @@ declare module '@inertiajs/core' {
|
|||||||
name: string;
|
name: string;
|
||||||
auth: Auth;
|
auth: Auth;
|
||||||
sidebarOpen: boolean;
|
sidebarOpen: boolean;
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,5 +9,4 @@ export type AvailableGitRepositorySourceModel = {
|
|||||||
account_name: string | null;
|
account_name: string | null;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
repositories: GitRepositoryOptionModel[];
|
repositories: GitRepositoryOptionModel[];
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,5 +8,4 @@ export type BoardColumnModel = {
|
|||||||
tickets?: TicketModel[];
|
tickets?: TicketModel[];
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
export type BranchNameValidationModel = {
|
export type BranchNameValidationModel = {
|
||||||
is_valid: boolean;
|
is_valid: boolean;
|
||||||
message: string;
|
message: string;
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,5 +5,4 @@ export type GitRepositoryOptionModel = {
|
|||||||
full_name: string;
|
full_name: string;
|
||||||
repository_url: string;
|
repository_url: string;
|
||||||
visibility_label: string;
|
visibility_label: string;
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,5 +12,4 @@ export type RepositoryConnectionModel = {
|
|||||||
has_personal_access: boolean;
|
has_personal_access: boolean;
|
||||||
created_at: string | null;
|
created_at: string | null;
|
||||||
updated_at: string | null;
|
updated_at: string | null;
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,5 +7,4 @@ export type TeamModel = {
|
|||||||
users: UserModel[];
|
users: UserModel[];
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
export type TicketBranchModel = {
|
export type TicketBranchModel = {
|
||||||
name: string;
|
name: string;
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,5 +6,4 @@ export type TicketFieldDefinitionModel = {
|
|||||||
project_id: number;
|
project_id: number;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,5 +11,4 @@ export type TicketFieldValueModel = {
|
|||||||
value_user_id: number | null;
|
value_user_id: number | null;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,5 +10,4 @@ export type TicketLinkModel = {
|
|||||||
ticket_link_type_id: number;
|
ticket_link_type_id: number;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,5 +5,4 @@ export type TicketLinkTypeModel = {
|
|||||||
project_id: number;
|
project_id: number;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,5 +23,4 @@ export type TicketModel = {
|
|||||||
worklog_entries?: TicketWorklogEntryModel[];
|
worklog_entries?: TicketWorklogEntryModel[];
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,5 +5,4 @@ export type TicketPriorityModel = {
|
|||||||
project_id: number;
|
project_id: number;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,5 +4,4 @@ export type TicketPullRequestModel = {
|
|||||||
source_branch: string;
|
source_branch: string;
|
||||||
status: string;
|
status: string;
|
||||||
title: string;
|
title: string;
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,5 +5,4 @@ export type TicketRepositoryBranchModel = {
|
|||||||
branches: TicketBranchModel[];
|
branches: TicketBranchModel[];
|
||||||
error: string | null;
|
error: string | null;
|
||||||
repository_connection: RepositoryConnectionModel;
|
repository_connection: RepositoryConnectionModel;
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,5 +5,4 @@ export type TicketRepositoryPullRequestModel = {
|
|||||||
error: string | null;
|
error: string | null;
|
||||||
pull_requests: TicketPullRequestModel[];
|
pull_requests: TicketPullRequestModel[];
|
||||||
repository_connection: RepositoryConnectionModel;
|
repository_connection: RepositoryConnectionModel;
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,5 +7,4 @@ export type TicketTypeModel = {
|
|||||||
system_key: string | null;
|
system_key: string | null;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,5 +10,4 @@ export type TicketWorklogEntryModel = {
|
|||||||
summary: string;
|
summary: string;
|
||||||
ticket_id: number;
|
ticket_id: number;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,5 +9,4 @@ export type UserGitConnectionModel = {
|
|||||||
account_name: string | null;
|
account_name: string | null;
|
||||||
created_at: string | null;
|
created_at: string | null;
|
||||||
updated_at: string | null;
|
updated_at: string | null;
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,5 +7,4 @@ export type UserModel = {
|
|||||||
is_admin: boolean;
|
is_admin: boolean;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user