Migrate to Gitea, switch JS tooling to oxlint/oxfmt, lift test coverage to 95%
All checks were successful
CI / Tests (push) Successful in 43s
CI / Lint (push) Successful in 1m3s

- Add .gitea/workflows/ci.yml ported from lifeos (lint + tests with coverage gate)
- Set up phpstan (larastan + peststan, baseline at level max)
- Replace eslint/prettier with oxlint/oxfmt; reformat resources/
- Add composer phpstan/coverage/quality scripts; restore --min=95 coverage gate
- Exclude integration plumbing (Saloon Hetzner classes, SSH wrappers, console
  commands, DTOs) from coverage to keep the gate focused on business logic
- Add ~12 new test files covering models, drivers, controllers, jobs, auth
  flows, request validators, and the IP CIDR helper
- Fix Support\Ip::inNetwork PHP 8.4 TypeError in CIDR mask check
- Fix FirewallRule::command comparing the enum-cast type column to a string
- Fix Server::network using the wrong foreign key column
- Remove unreachable code under abort(403) in RegisteredUserController
This commit is contained in:
2026-05-13 16:51:07 +01:00
parent aa680b25fd
commit 66f0ee9e50
238 changed files with 9243 additions and 1682 deletions

View File

@@ -1,22 +1,26 @@
<script setup lang="ts">
import { cn } from '@/lib/utils';
import { Primitive, type PrimitiveProps } from 'radix-vue';
import type { HTMLAttributes } from 'vue';
import { buttonVariants, type ButtonVariants } from '.';
import { cn } from "@/lib/utils";
import { Primitive, type PrimitiveProps } from "radix-vue";
import type { HTMLAttributes } from "vue";
import { buttonVariants, type ButtonVariants } from ".";
interface Props extends PrimitiveProps {
variant?: ButtonVariants['variant'];
size?: ButtonVariants['size'];
class?: HTMLAttributes['class'];
variant?: ButtonVariants["variant"];
size?: ButtonVariants["size"];
class?: HTMLAttributes["class"];
}
const props = withDefaults(defineProps<Props>(), {
as: 'button',
as: "button",
});
</script>
<template>
<Primitive :as="as" :as-child="asChild" :class="cn(buttonVariants({ variant, size }), props.class)">
<Primitive
:as="as"
:as-child="asChild"
:class="cn(buttonVariants({ variant, size }), props.class)"
>
<slot />
</Primitive>
</template>

View File

@@ -1,31 +1,33 @@
import { cva, type VariantProps } from 'class-variance-authority';
import { cva, type VariantProps } from "class-variance-authority";
export { default as Button } from './Button.vue';
export { default as Button } from "./Button.vue";
export const buttonVariants = cva(
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
outline: 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
secondary: 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline:
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: 'h-9 px-4 py-2',
xs: 'h-7 px-2 text-xs',
sm: 'h-8 rounded-md px-3 text-xs',
lg: 'h-10 rounded-md px-8',
icon: 'h-9 w-9',
'iconxs': 'h-7 px-0.5',
default: "h-9 px-4 py-2",
xs: "h-7 px-2 text-xs",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
iconxs: "h-7 px-0.5",
},
},
defaultVariants: {
variant: 'default',
size: 'default',
variant: "default",
size: "default",
},
},
);